/******************************************************************************* * Copyright (c) 2007 IBM Corporation. * 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: * Robert Fuhrer (rfuhrer@watson.ibm.com) - initial API and implementation *******************************************************************************/ package org.eclipse.imp.editor; import java.io.File; import java.io.InputStream; 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 java.util.ResourceBundle; import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.debug.ui.actions.IToggleBreakpointsTarget; import org.eclipse.debug.ui.actions.ToggleBreakpointAction; import org.eclipse.help.IContextProvider; import org.eclipse.imp.actions.OpenAction; import org.eclipse.imp.actions.QuickMenuAction; import org.eclipse.imp.actions.RulerEnableDisableBreakpointAction; import org.eclipse.imp.core.ErrorHandler; import org.eclipse.imp.editor.internal.AnnotationCreator; import org.eclipse.imp.editor.internal.EditorErrorTickUpdater; import org.eclipse.imp.editor.internal.FoldingController; import org.eclipse.imp.editor.internal.ProblemMarkerManager; import org.eclipse.imp.editor.internal.ToggleBreakpointsAdapter; import org.eclipse.imp.help.IMPHelp; import org.eclipse.imp.language.ILanguageService; import org.eclipse.imp.language.Language; import org.eclipse.imp.language.LanguageRegistry; import org.eclipse.imp.language.ServiceFactory; import org.eclipse.imp.model.ISourceProject; import org.eclipse.imp.model.ModelFactory; import org.eclipse.imp.model.ModelFactory.ModelException; import org.eclipse.imp.parser.IMessageHandler; import org.eclipse.imp.parser.IModelListener; import org.eclipse.imp.parser.IParseController; import org.eclipse.imp.parser.ISourcePositionLocator; import org.eclipse.imp.preferences.IPreferencesService; import org.eclipse.imp.preferences.PreferenceCache; import org.eclipse.imp.preferences.PreferenceConstants; import org.eclipse.imp.preferences.PreferencesService; import org.eclipse.imp.preferences.IPreferencesService.BooleanPreferenceListener; import org.eclipse.imp.preferences.IPreferencesService.IntegerPreferenceListener; import org.eclipse.imp.preferences.IPreferencesService.PreferenceServiceListener; import org.eclipse.imp.preferences.IPreferencesService.StringPreferenceListener; import org.eclipse.imp.runtime.RuntimePlugin; import org.eclipse.imp.services.IASTFindReplaceTarget; import org.eclipse.imp.services.IAnnotationTypeInfo; import org.eclipse.imp.services.IDocumentationProvider; import org.eclipse.imp.services.IEditorService; import org.eclipse.imp.services.ILanguageActionsContributor; import org.eclipse.imp.services.ILanguageSyntaxProperties; import org.eclipse.imp.services.IOccurrenceMarker; import org.eclipse.imp.services.IRefactoringContributor; import org.eclipse.imp.services.IToggleBreakpointsHandler; import org.eclipse.imp.services.ITokenColorer; import org.eclipse.imp.services.base.DefaultAnnotationHover; import org.eclipse.imp.services.base.TreeModelBuilderBase; import org.eclipse.imp.ui.DefaultPartListener; import org.eclipse.imp.ui.textPresentation.HTMLTextPresenter; import org.eclipse.imp.utils.StreamUtils; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IStatusLineManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.commands.ActionHandler; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.preference.PreferenceConverter; import org.eclipse.jface.resource.FontRegistry; import org.eclipse.jface.text.AbstractInformationControlManager; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.DefaultInformationControl; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IAutoEditStrategy; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentListener; import org.eclipse.jface.text.IInformationControl; import org.eclipse.jface.text.IInformationControlCreator; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextDoubleClickStrategy; import org.eclipse.jface.text.ITextHover; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.ITextViewerExtension; import org.eclipse.jface.text.ITextViewerExtension5; import org.eclipse.jface.text.ITypedRegion; import org.eclipse.jface.text.IUndoManager; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.TextPresentation; import org.eclipse.jface.text.TextUtilities; import org.eclipse.jface.text.contentassist.ContentAssistant; import org.eclipse.jface.text.contentassist.IContentAssistant; import org.eclipse.jface.text.formatter.ContentFormatter; import org.eclipse.jface.text.formatter.IContentFormatter; import org.eclipse.jface.text.hyperlink.IHyperlinkDetector; import org.eclipse.jface.text.hyperlink.IHyperlinkPresenter; import org.eclipse.jface.text.information.IInformationPresenter; import org.eclipse.jface.text.information.IInformationProvider; import org.eclipse.jface.text.information.IInformationProviderExtension; import org.eclipse.jface.text.information.InformationPresenter; import org.eclipse.jface.text.presentation.IPresentationDamager; import org.eclipse.jface.text.presentation.IPresentationReconciler; import org.eclipse.jface.text.presentation.IPresentationRepairer; import org.eclipse.jface.text.presentation.PresentationReconciler; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.AnnotationModel; import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; import org.eclipse.jface.text.source.IAnnotationHover; import org.eclipse.jface.text.source.IAnnotationModel; import org.eclipse.jface.text.source.IAnnotationModelListener; import org.eclipse.jface.text.source.ICharacterPairMatcher; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.IVerticalRuler; import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; 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.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.custom.VerifyKeyListener; import org.eclipse.swt.events.VerifyEvent; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.IPageLayout; import org.eclipse.ui.IURIEditorInput; import org.eclipse.ui.IViewPart; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.SubActionBars; import org.eclipse.ui.actions.ActionContext; import org.eclipse.ui.editors.text.StorageDocumentProvider; import org.eclipse.ui.editors.text.TextEditor; import org.eclipse.ui.editors.text.TextSourceViewerConfiguration; import org.eclipse.ui.handlers.IHandlerActivation; import org.eclipse.ui.handlers.IHandlerService; import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants; import org.eclipse.ui.texteditor.AbstractTextEditor; import org.eclipse.ui.texteditor.ContentAssistAction; import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.texteditor.IEditorStatusLine; import org.eclipse.ui.texteditor.ITextEditorActionConstants; import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; import org.eclipse.ui.texteditor.MarkerAnnotation; import org.eclipse.ui.texteditor.SourceViewerDecorationSupport; import org.eclipse.ui.texteditor.TextOperationAction; import org.eclipse.ui.texteditor.spelling.SpellingService; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; /** * An Eclipse editor, which is not enhanced using API; rather, we publish extension * points for outline, content assist, hover help, etc. * * @author Chris Laffra * @author Robert M. Fuhrer */ public class UniversalEditor extends TextEditor implements IASTFindReplaceTarget { /** * Action definition ID of the Edit -> Format Source action * (value <code>"org.eclipse.imp.runtime.editor.formatSource"</code>). */ public static final String FORMAT_SOURCE_COMMAND= RuntimePlugin.IMP_RUNTIME + ".editor.formatSource"; /** * Action definition ID of the Edit -> Toggle Comment action * (value <code>"org.eclipse.imp.runtime.editor.toggleComment"</code>). */ public static final String TOGGLE_COMMENT_COMMAND= RuntimePlugin.IMP_RUNTIME + ".editor.toggleComment"; /** * Action definition ID of the Edit -> Show Outline action * (value <code>"org.eclipse.imp.runtime.editor.showOutline"</code>). */ public static final String SHOW_OUTLINE_COMMAND= RuntimePlugin.IMP_RUNTIME + ".editor.showOutline"; /** * Action definition ID of the Edit -> Indent Selection action * (value <code>"org.eclipse.imp.runtime.editor.indentSelection"</code>). */ public static final String INDENT_SELECTION_COMMAND= RuntimePlugin.IMP_RUNTIME + ".editor.indentSelection"; /** * Action definition ID of the Edit -> Select Enclosing action * (value <code>"org.eclipse.imp.runtime.editor.selectEnclosing"</code>). */ public static final String SELECT_ENCLOSING_COMMAND= RuntimePlugin.IMP_RUNTIME + ".editor.selectEnclosing"; /** * Action definition ID of the edit -> Go to Matching Fence action * (value <code>"org.eclipse.imp.runtime.gotoMatchingFence"</code>). */ public static final String GOTO_MATCHING_FENCE_COMMAND= RuntimePlugin.IMP_RUNTIME + ".editor.gotoMatchingFence"; //$NON-NLS-1$ /** * Action definition ID of the edit -> Go to Previous Navigation Target action * (value <code>"org.eclipse.imp.runtime.editor.gotoPreviousTarget"</code>). */ public static final String GOTO_PREVIOUS_TARGET_COMMAND= RuntimePlugin.IMP_RUNTIME + ".editor.gotoPreviousTarget"; //$NON-NLS-1$ /** * Action definition ID of the edit -> Go to Next Navigation Target action * (value <code>"org.eclipse.imp.runtime.editor.gotoNextTarget"</code>). */ public static final String GOTO_NEXT_TARGET_COMMAND= RuntimePlugin.IMP_RUNTIME + ".editor.gotoNextTarget"; //$NON-NLS-1$ public static final String MESSAGE_BUNDLE= "org.eclipse.imp.editor.messages"; public static final String EDITOR_ID= RuntimePlugin.IMP_RUNTIME + ".impEditor"; public static final String PARSE_ANNOTATION_TYPE= "org.eclipse.imp.editor.parseAnnotation"; /** 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; public Language fLanguage; public ParserScheduler fParserScheduler; protected LanguageServiceManager fLanguageServiceManager; protected ServiceControllerManager fServiceControllerManager; private ProjectionAnnotationModel fAnnotationModel; private ProblemMarkerManager fProblemMarkerManager; private ICharacterPairMatcher fBracketMatcher; private SubActionBars fActionBars; private DefaultPartListener fRefreshContributions; private IPreferencesService fLangSpecificPrefs; private PreferenceServiceListener fFontListener; private PreferenceServiceListener fTabListener; private PreferenceServiceListener fSpacesForTabsListener; private IPropertyChangeListener fPropertyListener; private ToggleBreakpointAction fToggleBreakpointAction; private IAction fEnableDisableBreakpointAction; private ToggleBreakpointsAdapter fBreakpointHandler; private IResourceChangeListener fResourceListener; private IDocumentListener fDocumentListener; private FoldingActionGroup fFoldingActionGroup; private GenerateActionGroup fGenerateActionGroup; private static final String BUNDLE_FOR_CONSTRUCTED_KEYS= MESSAGE_BUNDLE;//$NON-NLS-1$ private static final String IMP_EDITOR_CONTEXT= RuntimePlugin.IMP_RUNTIME + ".imp_editor_context"; public static ResourceBundle fgBundleForConstructedKeys= ResourceBundle.getBundle(BUNDLE_FOR_CONSTRUCTED_KEYS); public static final String IMP_CODING_ACTION_SET = RuntimePlugin.IMP_RUNTIME + ".codingActionSet"; public UniversalEditor() { // RuntimePlugin.EDITOR_START_TIME= System.currentTimeMillis(); if (PreferenceCache.emitMessages) RuntimePlugin.getInstance().writeInfoMsg("Creating UniversalEditor instance"); // SMS 4 Apr 2007 // Do not set preference store with store obtained from plugin; one is // already defined for the parent text editor and populated with relevant // preferences // setPreferenceStore(RuntimePlugin.getInstance().getPreferenceStore()); setSourceViewerConfiguration(new StructuredSourceViewerConfiguration(getPreferenceStore())); configureInsertMode(SMART_INSERT, true); setInsertMode(SMART_INSERT); fProblemMarkerManager= new ProblemMarkerManager(); } @SuppressWarnings("unchecked") public Object getAdapter(Class required) { if (IContentOutlinePage.class.equals(required)) { return fServiceControllerManager == null ? null : fServiceControllerManager.getOutlineController(); } if (IToggleBreakpointsTarget.class.equals(required)) { IToggleBreakpointsHandler bkptHandler = fLanguageServiceManager == null ? null : fLanguageServiceManager.getToggleBreakpointsHandler(); if (bkptHandler != null) { if (fBreakpointHandler == null) { fBreakpointHandler= new ToggleBreakpointsAdapter(this, bkptHandler); } return fBreakpointHandler; } } if (IRegionSelectionService.class.equals(required)) { return fRegionSelector; } if (IContextProvider.class.equals(required)) { return IMPHelp.getHelpContextProvider(this, fLanguageServiceManager, IMP_EDITOR_CONTEXT); } return super.getAdapter(required); } protected void createActions() { super.createActions(); final ResourceBundle bundle= ResourceBundle.getBundle(MESSAGE_BUNDLE); Action action= new ContentAssistAction(bundle, "ContentAssistProposal.", this); action.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS); setAction("ContentAssistProposal", action); markAsStateDependentAction("ContentAssistProposal", true); // Not sure how to hook this up - the following class has all the right enablement logic, // but it doesn't implement IAction... How to register it as an action here??? // fToggleBreakpointAction= new AbstractRulerActionDelegate() { // protected IAction createAction(ITextEditor editor, IVerticalRulerInfo rulerInfo) { // return new ToggleBreakpointAction(UniversalEditor.this, getDocumentProvider().getDocument(getEditorInput()), getVerticalRuler()); // } // } fToggleBreakpointAction= new ToggleBreakpointAction(this, getDocumentProvider().getDocument(getEditorInput()), getVerticalRuler()); setAction("ToggleBreakpoint", action); fEnableDisableBreakpointAction= new RulerEnableDisableBreakpointAction(this, getVerticalRuler()); setAction("ToggleBreakpoint", action); action= new TextOperationAction(bundle, "Format.", this, ISourceViewer.FORMAT); //$NON-NLS-1$ action.setActionDefinitionId(FORMAT_SOURCE_COMMAND); setAction("Format", action); //$NON-NLS-1$ markAsStateDependentAction("Format", true); //$NON-NLS-1$ markAsSelectionDependentAction("Format", true); //$NON-NLS-1$ // PlatformUI.getWorkbench().getHelpSystem().setHelp(action, IJavaHelpContextIds.FORMAT_ACTION); action= new TextOperationAction(bundle, "ShowOutline.", this, StructuredSourceViewer.SHOW_OUTLINE, true /* runsOnReadOnly */); //$NON-NLS-1$ action.setActionDefinitionId(SHOW_OUTLINE_COMMAND); setAction(SHOW_OUTLINE_COMMAND, action); //$NON-NLS-1$ // PlatformUI.getWorkbench().getHelpSystem().setHelp(action, IJavaHelpContextIds.SHOW_OUTLINE_ACTION); action= new TextOperationAction(bundle, "ToggleComment.", this, StructuredSourceViewer.TOGGLE_COMMENT); //$NON-NLS-1$ action.setActionDefinitionId(TOGGLE_COMMENT_COMMAND); setAction(TOGGLE_COMMENT_COMMAND, action); //$NON-NLS-1$ // PlatformUI.getWorkbench().getHelpSystem().setHelp(action, IJavaHelpContextIds.TOGGLE_COMMENT_ACTION); action= new TextOperationAction(bundle, "IndentSelection.", this, StructuredSourceViewer.INDENT_SELECTION); //$NON-NLS-1$ action.setActionDefinitionId(INDENT_SELECTION_COMMAND); setAction(INDENT_SELECTION_COMMAND, action); //$NON-NLS-1$ action= new GotoMatchingFenceAction(this); action.setActionDefinitionId(GOTO_MATCHING_FENCE_COMMAND); setAction(GOTO_MATCHING_FENCE_COMMAND, action); action= new GotoPreviousTargetAction(this); action.setActionDefinitionId(GOTO_PREVIOUS_TARGET_COMMAND); setAction(GOTO_PREVIOUS_TARGET_COMMAND, action); action= new GotoNextTargetAction(this); action.setActionDefinitionId(GOTO_NEXT_TARGET_COMMAND); setAction(GOTO_NEXT_TARGET_COMMAND, action); action= new SelectEnclosingAction(this); action.setActionDefinitionId(SELECT_ENCLOSING_COMMAND); setAction(SELECT_ENCLOSING_COMMAND, action); fFoldingActionGroup= new FoldingActionGroup(this, this.getSourceViewer()); fGenerateActionGroup= new GenerateActionGroup(this, ITextEditorActionConstants.GROUP_EDIT); installQuickAccessAction(); } private QuickMenuAction fQuickAccessAction; private IHandlerActivation fQuickAccessHandlerActivation; private IHandlerService fHandlerService; private static final String QUICK_MENU_ID= "org.eclipse.imp.runtime.editor.refactor.quickMenu"; //$NON-NLS-1$ private final class AnnotationUpdater implements IProblemChangedListener { public void problemsChanged(IResource[] changedResources, boolean isMarkerChange) { // TODO Work-around to remove annotations that were resolved by changes to other resources. // It would be better to match the markers to the annotations, and decide which // annotations to remove. if (fParserScheduler != null) { fParserScheduler.schedule(50); } } } private class RefactorQuickAccessAction extends QuickMenuAction { public RefactorQuickAccessAction() { super(QUICK_MENU_ID); } protected void fillMenu(IMenuManager menu) { contributeRefactoringActions(menu); } } private void installQuickAccessAction() { fHandlerService= (IHandlerService) getSite().getService(IHandlerService.class); if (fHandlerService != null) { fQuickAccessAction= new RefactorQuickAccessAction(); fQuickAccessHandlerActivation= fHandlerService.activateHandler(fQuickAccessAction.getActionDefinitionId(), new ActionHandler(fQuickAccessAction)); } } protected void editorContextMenuAboutToShow(IMenuManager menu) { menu.add(new OpenAction(this)); super.editorContextMenuAboutToShow(menu); contributeRefactoringActions(menu); contributeLanguageActions(menu); ActionContext context= new ActionContext(getSelectionProvider().getSelection()); fGenerateActionGroup.setContext(context); fGenerateActionGroup.fillContextMenu(menu); fGenerateActionGroup.setContext(null); } private void contributeRefactoringActions(IMenuManager menu) { Set<IRefactoringContributor> contributors= fLanguageServiceManager.getRefactoringContributors(); if (contributors != null && !contributors.isEmpty()) { List<IAction> editorActions= new ArrayList<IAction>(); for (Iterator<IRefactoringContributor> iter= contributors.iterator(); iter.hasNext(); ) { IRefactoringContributor con= iter.next(); try { IAction[] conActions= con.getEditorRefactoringActions(this); for (int i= 0; i < conActions.length; i++) editorActions.add(conActions[i]); } catch (LinkageError e) { RuntimePlugin.getInstance().logException("Unable to create refactoring actions for contributor " + con, e); } catch (Exception e) { RuntimePlugin.getInstance().logException("Unable to create refactoring actions for contributor " + con, e); } } Separator refGroup= new Separator("group.refactor"); IMenuManager refMenu= new MenuManager("Refac&tor", "org.eclipse.imp.refactor"); menu.add(refGroup); menu.appendToGroup("group.refactor", refMenu); for (Iterator<IAction> actionIter= editorActions.iterator(); actionIter.hasNext(); ) { refMenu.add(actionIter.next()); } } } private void contributeLanguageActions(IMenuManager menu) { Set<ILanguageActionsContributor> actionContributors= fLanguageServiceManager.getActionContributors(); if (!actionContributors.isEmpty()) { menu.add(new Separator()); } for(ILanguageActionsContributor con : actionContributors) { try { con.contributeToEditorMenu(this, menu); } catch (LinkageError e) { RuntimePlugin.getInstance().logException("Unable to create editor actions for contributor " + con, e); } catch(Exception e) { RuntimePlugin.getInstance().logException("Unable to create editor actions for contributor " + con, e); } } } /* (non-Javadoc) * @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#isOverviewRulerVisible() */ protected boolean isOverviewRulerVisible() { return true; } protected void rulerContextMenuAboutToShow(IMenuManager menu) { addDebugActions(menu); super.rulerContextMenuAboutToShow(menu); IMenuManager foldingMenu= new MenuManager("Folding", "projection"); //$NON-NLS-1$ menu.appendToGroup(ITextEditorActionConstants.GROUP_RULERS, foldingMenu); IAction action; // action= getAction("FoldingToggle"); //$NON-NLS-1$ // foldingMenu.add(action); action= getAction("FoldingExpandAll"); //$NON-NLS-1$ foldingMenu.add(action); action= getAction("FoldingCollapseAll"); //$NON-NLS-1$ foldingMenu.add(action); action= getAction("FoldingRestore"); //$NON-NLS-1$ foldingMenu.add(action); action= getAction("FoldingCollapseMembers"); //$NON-NLS-1$ foldingMenu.add(action); action= getAction("FoldingCollapseComments"); //$NON-NLS-1$ foldingMenu.add(action); } private void addDebugActions(IMenuManager menu) { menu.add(fToggleBreakpointAction); menu.add(fEnableDisableBreakpointAction); } /** * Sets the given message as error message to this editor's status line. * * @param msg message to be set */ protected void setStatusLineErrorMessage(String msg) { 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 */ protected void setStatusLineMessage(String msg) { IEditorStatusLine statusLine= (IEditorStatusLine) getAdapter(IEditorStatusLine.class); if (statusLine != null) statusLine.setMessage(false, msg, null); } public ProblemMarkerManager getProblemMarkerManager() { return fProblemMarkerManager; } public void updatedTitleImage(Image image) { setTitleImage(image); } /** * Jumps to the next enabled annotation according to the given direction. * An annotation type is enabled if it is configured to be in the * Next/Previous tool bar drop down menu and if it is checked. * * @param forward <code>true</code> if search direction is forward, <code>false</code> if backward */ public Annotation gotoAnnotation(boolean forward) { ITextSelection selection= (ITextSelection) getSelectionProvider().getSelection(); Position position= new Position(0, 0); Annotation annotation= getNextAnnotation(selection.getOffset(), selection.getLength(), forward, position); if (false /* delayed - see bug 18316 */) { selectAndReveal(position.getOffset(), position.getLength()); } else /* no delay - see bug 18316 */{ setStatusLineErrorMessage(null); setStatusLineMessage(null); if (annotation != null) { updateAnnotationViews(annotation); selectAndReveal(position.getOffset(), position.getLength()); setStatusLineMessage(annotation.getText()); } } return annotation; } /** * Returns the annotation closest to the given range respecting the given * direction. If an annotation is found, the annotations current position * is copied into the provided annotation position. * * @param offset the region offset * @param length the region length * @param forward <code>true</code> for forwards, <code>false</code> for backward * @param annotationPosition the position of the found annotation * @return the found annotation */ private Annotation getNextAnnotation(final int offset, final int length, boolean forward, Position annotationPosition) { Annotation nextAnnotation= null; Position nextAnnotationPosition= null; Annotation containingAnnotation= null; Position containingAnnotationPosition= null; boolean currentAnnotation= false; IDocument document= getDocumentProvider().getDocument(getEditorInput()); int endOfDocument= document.getLength(); int distance= Integer.MAX_VALUE; IAnnotationModel model= getDocumentProvider().getAnnotationModel(getEditorInput()); for(Iterator<Annotation> e= model.getAnnotationIterator(); e.hasNext(); ) { Annotation a= (Annotation) e.next(); // if ((a instanceof IJavaAnnotation) && ((IJavaAnnotation) a).hasOverlay() || !isNavigationTarget(a)) // continue; // TODO RMF 4/19/2006 - Need more accurate logic here for filtering annotations, particularly when we add support for other annotation types if (!(a instanceof MarkerAnnotation) && !a.getType().equals(PARSE_ANNOTATION_TYPE)) continue; Position p= model.getPosition(a); if (p == null) continue; if (forward && p.offset == offset || !forward && p.offset + p.getLength() == offset + length) {// || p.includes(offset)) { if (containingAnnotation == null || (forward && p.length >= containingAnnotationPosition.length || !forward && p.length >= containingAnnotationPosition.length)) { containingAnnotation= a; containingAnnotationPosition= p; currentAnnotation= p.length == length; } } else { int currentDistance= forward ? p.getOffset() - offset : offset + length - (p.getOffset() + p.length); if (currentDistance < 0) currentDistance= endOfDocument + currentDistance; if (currentDistance < distance || currentDistance == distance && p.length < nextAnnotationPosition.length) { distance= currentDistance; nextAnnotation= a; nextAnnotationPosition= p; } } } if (containingAnnotationPosition != null && (!currentAnnotation || nextAnnotation == null)) { annotationPosition.setOffset(containingAnnotationPosition.getOffset()); annotationPosition.setLength(containingAnnotationPosition.getLength()); return containingAnnotation; } if (nextAnnotationPosition != null) { annotationPosition.setOffset(nextAnnotationPosition.getOffset()); annotationPosition.setLength(nextAnnotationPosition.getLength()); } return nextAnnotation; } /** * Updates the annotation views that show the given annotation. * * @param annotation the annotation */ private void updateAnnotationViews(Annotation annotation) { IMarker marker= null; if (annotation instanceof MarkerAnnotation) marker= ((MarkerAnnotation) annotation).getMarker(); else if (marker != null /* && !marker.equals(fLastMarkerTarget) */) { try { boolean isProblem= marker.isSubtypeOf(IMarker.PROBLEM); IWorkbenchPage page= getSite().getPage(); IViewPart view= page.findView(isProblem ? IPageLayout.ID_PROBLEM_VIEW : IPageLayout.ID_TASK_LIST); //$NON-NLS-1$ //$NON-NLS-2$ if (view != null) { Method method= view.getClass().getMethod("setSelection", new Class[] { IStructuredSelection.class, boolean.class }); //$NON-NLS-1$ method.invoke(view, new Object[] { new StructuredSelection(marker), Boolean.TRUE }); } } catch (CoreException x) { } catch (NoSuchMethodException x) { } catch (IllegalAccessException x) { } catch (InvocationTargetException x) { } // ignore exceptions, don't update any of the lists, just set status line } } private IDocumentProvider fZipDocProvider; @Override public IDocumentProvider getDocumentProvider() { if (getEditorInput() instanceof IURIEditorInput) { IURIEditorInput uriEditorInput = (IURIEditorInput) getEditorInput(); URI uri= uriEditorInput.getURI(); String path= uri.getPath(); if (path.contains(".jar:") || path.contains(".zip:")) { if (fZipDocProvider == null) { fZipDocProvider= new StorageDocumentProvider() { @Override protected IAnnotationModel createAnnotationModel(Object element) throws CoreException { // If we don't do this, the resulting editor won't permit source folding return new AnnotationModel(); } @Override protected ElementInfo createElementInfo(Object element) throws CoreException { ElementInfo ei= super.createElementInfo(element); ei.fDocument= new Document(getZipEntryContents((IURIEditorInput) element)); return ei; } @Override protected boolean setDocumentContent(IDocument document, IEditorInput editorInput) throws CoreException { IURIEditorInput uriEditorInput = (IURIEditorInput) getEditorInput(); String contents = getZipEntryContents(uriEditorInput); document.set(contents); return true; } private String getZipEntryContents(IURIEditorInput uriEditorInput) { String contents= ""; try { URI uri= uriEditorInput.getURI(); String path= uri.getPath(); String jarPath= path.substring(0, path.indexOf(':')); String entryPath= path.substring(path.indexOf(':') + 1); ZipFile zipFile= new ZipFile(new File(jarPath)); ZipEntry entry= zipFile.getEntry(entryPath); InputStream is= zipFile.getInputStream(entry); contents= StreamUtils.readStreamContents(is); } catch (Exception e) { RuntimePlugin.getInstance().logException("Exception caught while obtaining contents of zip file entry", e); } return contents; } }; } return fZipDocProvider; } } return super.getDocumentProvider(); } public void createPartControl(Composite parent) { fLanguage= LanguageRegistry.findLanguage(getEditorInput(), getDocumentProvider()); // SMS 10 Oct 2008: null check added per bug #242949 if (fLanguage == null) { // throw new IllegalArgumentException("No language support found for files of type '" + // EditorInputUtils.getPath(getEditorInput()).getFileExtension() + "'"); } // Create language service extensions now, since some services could // get accessed via super.createPartControl() (in particular, while // setting up the ISourceViewer). if (fLanguage != null) { fLanguageServiceManager= new LanguageServiceManager(fLanguage); fLanguageServiceManager.initialize(this); fServiceControllerManager= new ServiceControllerManager(this, fLanguageServiceManager); fServiceControllerManager.initialize(); if (fLanguageServiceManager.getParseController() != null) { initializeParseController(); findLanguageSpecificPreferences(); } } // RMF 07 June 2010 - Not sure why the "run the spell checker" pref would get set, but // it does seem to, which gives lots of annoying squigglies all over the place... getPreferenceStore().setValue(SpellingService.PREFERENCE_SPELLING_ENABLED, false); super.createPartControl(parent); if (fLanguageServiceManager != null && fLanguageServiceManager.getParseController() != null) { fServiceControllerManager.setSourceViewer(getSourceViewer()); initiateServiceControllers(); } // SMS 4 Apr 2007: Call no longer needed because preferences for the // overview ruler are now obtained from appropriate preference store directly //setupOverviewRulerAnnotations(); // SMS 4 Apr 2007: Also should not need this, since we're not using // the plugin's store (for this purpose) //AbstractDecoratedTextEditorPreferenceConstants.initializeDefaultValues(RuntimePlugin.getInstance().getPreferenceStore()); setTitleImageFromLanguageIcon(); setSourceFontFromPreference(); setupBracketCloser(); setupSourcePrefListeners(); initializeEditorContributors(); // SMS 3 Oct 2008: moved call to watchDocument(..) to initiateServiceControllers(). watchForSourceMove(); } private void initializeParseController() { // Initialize the parse controller now, since the initialization of other things (like the context help support) might depend on it being so. IEditorInput editorInput= getEditorInput(); IFile file= EditorInputUtils.getFile(editorInput); IPath filePath= EditorInputUtils.getPath(editorInput); try { IProject project= (file != null && file.exists()) ? file.getProject() : null; ISourceProject srcProject= (project != null) ? ModelFactory.open(project) : null; fLanguageServiceManager.getParseController().initialize(filePath, srcProject, fAnnotationCreator); // TODO Need to do the following to give the strategy access to project-specific preference settings // if (fLanguageServiceManager.getAutoEditStrategies().size() > 0) { // Set<org.eclipse.imp.services.IAutoEditStrategy> strategies= fLanguageServiceManager.getAutoEditStrategies(); // for(org.eclipse.imp.services.IAutoEditStrategy strategy: strategies) { // strategy.setProject(project); // } // } } catch (ModelException e) { ErrorHandler.reportError("Error initializing parser for input " + editorInput.getName() + ":", e); } } private void findLanguageSpecificPreferences() { ISourceProject srcProject = fLanguageServiceManager.getParseController().getProject(); if (srcProject != null) { IProject project= srcProject.getRawProject(); fLangSpecificPrefs= new PreferencesService(project, fLanguage.getName()); } else { fLangSpecificPrefs= new PreferencesService(null, fLanguage.getName()); } // Now propagate the setting of "spaces for tabs" from either the language-specific preference store, // or the IMP runtime's preference store to the UniversalEditor's preference store, where // AbstractDecoratedTextEditor.isTabsToSpacesConversionEnabled() will look. boolean spacesForTabs= RuntimePlugin.getInstance().getPreferenceStore().getBoolean(PreferenceConstants.P_SPACES_FOR_TABS); getPreferenceStore().setValue(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SPACES_FOR_TABS, spacesForTabs); } private void setupSourcePrefListeners() { // If there are no language-specific preferences, use the settings on the IMP preferences page if (fLangSpecificPrefs == null || !fLangSpecificPrefs.isDefined(PreferenceConstants.P_SOURCE_FONT) || !fLangSpecificPrefs.isDefined(PreferenceConstants.P_TAB_WIDTH) || !fLangSpecificPrefs.isDefined(PreferenceConstants.P_SPACES_FOR_TABS)) { fPropertyListener= new IPropertyChangeListener() { public void propertyChange(PropertyChangeEvent event) { if (event.getProperty().equals(PreferenceConstants.P_SOURCE_FONT) && !fLangSpecificPrefs.isDefined(PreferenceConstants.P_SOURCE_FONT)) { FontData[] newValue= (FontData[]) event.getNewValue(); String fontDescriptor= newValue[0].toString(); handleFontChange(newValue, fontDescriptor); } else if (event.getProperty().equals(PreferenceConstants.P_TAB_WIDTH) && !fLangSpecificPrefs.isDefined(PreferenceConstants.P_TAB_WIDTH)) { handleTabsChange(((Integer) event.getNewValue()).intValue()); } else if (event.getProperty().equals(PreferenceConstants.P_SPACES_FOR_TABS) && !fLangSpecificPrefs.isDefined(PreferenceConstants.P_SPACES_FOR_TABS)) { handleSpacesForTabsChange(((Boolean) event.getNewValue()).booleanValue()); } } }; RuntimePlugin.getInstance().getPreferenceStore().addPropertyChangeListener(fPropertyListener); } // TODO Perhaps add a flavor of IMP PreferenceListener that notifies for a change to any preference key? // Then the following listeners could become just one, at the expense of casting the pref values. if (fLangSpecificPrefs != null) { fFontListener= new StringPreferenceListener(fLangSpecificPrefs, PreferenceConstants.P_SOURCE_FONT) { @Override public void changed(String oldValue, String newValue) { FontData[] fontData= PreferenceConverter.readFontData(newValue); handleFontChange(fontData, newValue); } }; } if (fLangSpecificPrefs != null) { fTabListener= new IntegerPreferenceListener(fLangSpecificPrefs, PreferenceConstants.P_TAB_WIDTH) { @Override public void changed(int oldValue, int newValue) { handleTabsChange(newValue); } }; } if (fLangSpecificPrefs != null) { fSpacesForTabsListener= new BooleanPreferenceListener(fLangSpecificPrefs, PreferenceConstants.P_SPACES_FOR_TABS) { @Override public void changed(boolean oldValue, boolean newValue) { handleSpacesForTabsChange(newValue); } }; } } private void handleTabsChange(int newTab) { if (getSourceViewer() != null) { getSourceViewer().getTextWidget().setTabs(newTab); } } private void handleSpacesForTabsChange(boolean newValue) { if (getSourceViewer() == null) { return; } if (newValue) { installTabsToSpacesConverter(); } else { uninstallTabsToSpacesConverter(); } // Apparently un/installing the tabs-to-spaces converter isn't enough - shift left/right needs // the "indent prefixes" to be computed properly, which relies on the preference store having // the right value for AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SPACES_FOR_TABS. getPreferenceStore().setValue(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SPACES_FOR_TABS, newValue); } private void handleFontChange(FontData[] fontData, String fontDescriptor) { FontRegistry fontRegistry= RuntimePlugin.getInstance().getFontRegistry(); if (!fontRegistry.hasValueFor(fontDescriptor)) { fontRegistry.put(fontDescriptor, fontData); } Font sourceFont= fontRegistry.get(fontDescriptor); if (sourceFont != null && getSourceViewer() != null) { getSourceViewer().getTextWidget().setFont(sourceFont); } } private void watchDocument(final long reparse_schedule_delay) { if (fLanguageServiceManager.getParseController() == null) { return; } IDocument doc= getDocumentProvider().getDocument(getEditorInput()); doc.addDocumentListener(fDocumentListener= new IDocumentListener() { public void documentAboutToBeChanged(DocumentEvent event) {} public void documentChanged(DocumentEvent event) { // System.out.println("Document change event @ offset " + event.fOffset + " & length " + event.fLength); fParserScheduler.cancel(); fParserScheduler.schedule(reparse_schedule_delay); } }); } private class BracketInserter implements VerifyKeyListener { private final Map<String,String> fFencePairs= new HashMap<String, String>(); private final String fOpenFences; private final Map<Character,Boolean> fCloseFenceMap= new HashMap<Character, Boolean>(); // private final String CATEGORY= toString(); // private IPositionUpdater fUpdater= new ExclusivePositionUpdater(CATEGORY); public BracketInserter() { String[][] pairs= fLanguageServiceManager.getParseController().getSyntaxProperties().getFences(); StringBuilder sb= new StringBuilder(); for(int i= 0; i < pairs.length; i++) { sb.append(pairs[i][0]); fFencePairs.put(pairs[i][0], pairs[i][1]); } fOpenFences= sb.toString(); } public void setCloseFenceEnabled(char openingFence, boolean enabled) { fCloseFenceMap.put(openingFence, enabled); } public void setCloseFencesEnabled(boolean enabled) { for(int i= 0; i < fOpenFences.length(); i++) { fCloseFenceMap.put(fOpenFences.charAt(i), enabled); } } /* * @see org.eclipse.swt.custom.VerifyKeyListener#verifyKey(org.eclipse.swt.events.VerifyEvent) */ public void verifyKey(VerifyEvent event) { // early pruning to slow down normal typing as little as possible if (!event.doit || getInsertMode() != SMART_INSERT) return; if (fOpenFences.indexOf(event.character) < 0) { return; } final ISourceViewer sourceViewer= getSourceViewer(); IDocument document= sourceViewer.getDocument(); final Point selection= sourceViewer.getSelectedRange(); final int offset= selection.x; final int length= selection.y; try { // IRegion startLine= document.getLineInformationOfOffset(offset); // IRegion endLine= document.getLineInformationOfOffset(offset + length); // TODO Ask the parser/scanner whether the close fence is valid here // (i.e. whether it would recover by inserting the matching close fence character) // Right now, naively insert the closing fence regardless. ITypedRegion partition= TextUtilities.getPartition(document, getSourceViewerConfiguration().getConfiguredDocumentPartitioning(sourceViewer), offset, true); if (!IDocument.DEFAULT_CONTENT_TYPE.equals(partition.getType())) return; if (!validateEditorInputState()) return; final String inputStr= new String(new char[] { event.character }); final String closingFence= fFencePairs.get(inputStr); final StringBuffer buffer= new StringBuffer(); buffer.append(inputStr); buffer.append(closingFence); document.replace(offset, length, buffer.toString()); sourceViewer.setSelectedRange(offset + inputStr.length(), 0); event.doit= false; } catch (BadLocationException e) { RuntimePlugin.getInstance().logException(e.getMessage(), e); } } } private BracketInserter fBracketInserter; private final String CLOSE_FENCES= PreferenceConstants.EDITOR_CLOSE_FENCES; private void setupBracketCloser() { if (true) return; // Bug #536: Disable for now, until we can be more intelligent about when to overwrite an existing (subsequent) close-fence char. IParseController parseController= fLanguageServiceManager.getParseController(); if (parseController == null || parseController.getSyntaxProperties() == null || parseController.getSyntaxProperties().getFences() == null) { return; } /** Preference key for automatically closing brackets and parenthesis */ boolean closeFences= fLangSpecificPrefs.getBooleanPreference(CLOSE_FENCES); // false if no lang-specific setting fBracketInserter= new BracketInserter(); fBracketInserter.setCloseFencesEnabled(closeFences); ISourceViewer sourceViewer= getSourceViewer(); if (sourceViewer instanceof ITextViewerExtension) ((ITextViewerExtension) sourceViewer).prependVerifyKeyListener(fBracketInserter); } private void watchForSourceMove() { if (fLanguageServiceManager == null || fLanguageServiceManager.getParseController() == null || fLanguageServiceManager.getParseController().getProject() == null) { return; } ResourcesPlugin.getWorkspace().addResourceChangeListener(fResourceListener= new IResourceChangeListener() { public void resourceChanged(IResourceChangeEvent event) { if (event.getType() != IResourceChangeEvent.POST_CHANGE) return; IParseController pc= fLanguageServiceManager.getParseController(); if (pc == null) { return; } IPath oldWSRelPath= pc.getProject().getRawProject().getFullPath().append(pc.getPath()); IResourceDelta rd= event.getDelta().findMember(oldWSRelPath); if (rd != null) { if ((rd.getFlags() & IResourceDelta.MOVED_TO) == IResourceDelta.MOVED_TO) { IPath newPath= rd.getMovedToPath(); IPath newProjRelPath= newPath.removeFirstSegments(1); String newProjName= newPath.segment(0); boolean sameProj= pc.getProject().getRawProject().getName().equals(newProjName); try { ISourceProject proj= sameProj ? pc.getProject() : ModelFactory.open(ResourcesPlugin.getWorkspace().getRoot().getProject(newProjName)); // Tell the IParseController about the move - it caches the path // fParserScheduler.cancel(); // avoid a race condition if ParserScheduler was starting/in the middle of a run pc.initialize(newProjRelPath, proj, fAnnotationCreator); } catch (ModelException e) { RuntimePlugin.getInstance().logException("Error tracking resource move", e); } } } } }); } private void setSourceFontFromPreference() { String fontName= null; if (fLangSpecificPrefs != null) { fontName= fLangSpecificPrefs.getStringPreference(PreferenceConstants.P_SOURCE_FONT); } if (fontName == null) { IPreferenceStore prefStore= RuntimePlugin.getInstance().getPreferenceStore(); fontName= prefStore.getString(PreferenceConstants.P_SOURCE_FONT); } FontRegistry fontRegistry= RuntimePlugin.getInstance().getFontRegistry(); if (!fontRegistry.hasValueFor(fontName)) { fontRegistry.put(fontName, PreferenceConverter.readFontData(fontName)); } Font sourceFont= fontRegistry.get(fontName); if (sourceFont != null) { getSourceViewer().getTextWidget().setFont(sourceFont); } } private void initiateServiceControllers() { try { StructuredSourceViewer sourceViewer= (StructuredSourceViewer) getSourceViewer(); if (PreferenceCache.emitMessages) { RuntimePlugin.getInstance().writeInfoMsg( "Creating language service controllers for " + fLanguage.getName()); } fEditorErrorTickUpdater= new EditorErrorTickUpdater(this); fProblemMarkerManager.addListener(fEditorErrorTickUpdater); fAnnotationUpdater= new AnnotationUpdater(); fProblemMarkerManager.addListener(fAnnotationUpdater); fParserScheduler= new ParserScheduler(fLanguageServiceManager.getParseController(), this, getDocumentProvider(), fAnnotationCreator); // The source viewer configuration has already been asked for its ITextHover, // but before we actually instantiated the relevant controller class. So update // the source viewer, now that we actually have the hover provider. sourceViewer.setTextHover(fServiceControllerManager.getHoverHelpController(), IDocument.DEFAULT_CONTENT_TYPE); // The source viewer configuration has already been asked for its IContentFormatter, // but before we actually instantiated the relevant controller class. So update the // source viewer, now that we actually have the IContentFormatter. ContentFormatter formatter= new ContentFormatter(); formatter.setFormattingStrategy(fServiceControllerManager.getFormattingController(), IDocument.DEFAULT_CONTENT_TYPE); sourceViewer.setFormatter(formatter); if (fLanguageServiceManager.getTokenColorer() != null) { try { fServiceControllerManager.getPresentationController().damage(new Region(0, sourceViewer.getDocument().getLength())); } catch (Exception e) { ErrorHandler.reportError("Error during initial damage repair", e); } } // SMS 29 May 2007 (to give viewer access to single-line comment prefix) sourceViewer.setParseController(fLanguageServiceManager.getParseController()); if (fLanguageServiceManager.getFoldingUpdater() != null) { ProjectionViewer projViewer= sourceViewer; ProjectionSupport projectionSupport= new ProjectionSupport(projViewer, getAnnotationAccess(), getSharedColors()); projectionSupport.install(); projViewer.doOperation(ProjectionViewer.TOGGLE); fAnnotationModel= projViewer.getProjectionAnnotationModel(); if (fAnnotationModel != null) { fParserScheduler.addModelListener(new FoldingController(fAnnotationModel, fLanguageServiceManager.getFoldingUpdater())); } } if (isEditable()) { fParserScheduler.addModelListener(new AnnotationCreatorListener()); } fServiceControllerManager.setupModelListeners(fParserScheduler); // TODO RMF 8/6/2007 - Disable "Mark Occurrences" if no occurrence marker exists for this language // The following doesn't work b/c getAction() doesn't find the Mark Occurrences action (why?) // if (this.fOccurrenceMarker == null) // getAction("org.eclipse.imp.runtime.actions.markOccurrencesAction").setEnabled(false); installExternalEditorServices(); watchDocument(REPARSE_SCHEDULE_DELAY); fParserScheduler.run(new NullProgressMonitor()); } catch (Exception e) { ErrorHandler.reportError("Error while creating service controllers", e); } } private static final int REPARSE_SCHEDULE_DELAY= 100; private void setTitleImageFromLanguageIcon() { // Only set the editor's title bar icon if the language has a label provider if (fLanguageServiceManager != null && fLanguageServiceManager.getLabelProvider() != null) { IEditorInput editorInput= getEditorInput(); IFile file= EditorInputUtils.getFile(editorInput); try { setTitleImage(fLanguageServiceManager.getLabelProvider().getImage(file)); } catch (Exception e) { ErrorHandler.reportError("Error while setting source editor title icon from label provider", e); } } } private void installExternalEditorServices() { Set<IModelListener> editorServices= fLanguageServiceManager.getEditorServices(); for(ILanguageService editorService : editorServices) { if (editorService instanceof IEditorService) ((IEditorService) editorService).setEditor(this); fParserScheduler.addModelListener((IModelListener) editorService); } } /** * Adds elements to toolbars, menubars and statusbars * */ private void initializeEditorContributors() { if (fLanguage != null) { addEditorActions(); registerEditorContributionsActivator(); } } /** * Makes sure that menu items and status bar items disappear as the editor * is out of focus, and reappear when it gets focus again. This does * not work for toolbar items for unknown reasons, they stay visible. * */ private void registerEditorContributionsActivator() { fRefreshContributions = new DefaultPartListener() { private UniversalEditor editor = UniversalEditor.this; @Override public void partActivated(IWorkbenchPart part) { if (part == editor) { editor.fActionBars.activate(); editor.fActionBars.updateActionBars(); } if (part instanceof UniversalEditor) { part.getSite().getPage().showActionSet(IMP_CODING_ACTION_SET); } else { part.getSite().getPage().hideActionSet(IMP_CODING_ACTION_SET); } } @Override public void partDeactivated(IWorkbenchPart part) { if (part == editor) { editor.fActionBars.deactivate(); editor.fActionBars.updateActionBars(); } } }; getSite().getPage().addPartListener(fRefreshContributions); } private void unregisterEditorContributionsActivator() { if (fRefreshContributions != null) { getSite().getPage().removePartListener(fRefreshContributions); } fRefreshContributions= null; } /** * Uses the LanguageActionsContributor extension point to add * elements to (sub) action bars. * */ private void addEditorActions() { final IActionBars allActionBars = getEditorSite().getActionBars(); if (fActionBars == null) { Set<ILanguageActionsContributor> contributors = ServiceFactory .getInstance().getLanguageActionsContributors(fLanguage); fActionBars = new SubActionBars(allActionBars); IStatusLineManager status = fActionBars.getStatusLineManager(); IToolBarManager toolbar = fActionBars.getToolBarManager(); IMenuManager menu = fActionBars.getMenuManager(); for (ILanguageActionsContributor c : contributors) { c.contributeToStatusLine(this, status); c.contributeToToolBar(this, toolbar); c.contributeToMenuBar(this, menu); } fActionBars.updateActionBars(); allActionBars.updateActionBars(); } allActionBars.updateActionBars(); } public void dispose() { if (fFontListener != null) { fFontListener.dispose(); } if (fTabListener != null) { fTabListener.dispose(); } if (fSpacesForTabsListener != null) { fSpacesForTabsListener.dispose(); } if (fPropertyListener != null) { RuntimePlugin.getInstance().getPreferenceStore().removePropertyChangeListener(fPropertyListener); } unregisterEditorContributionsActivator(); if (fEditorErrorTickUpdater != null) { fProblemMarkerManager.removeListener(fEditorErrorTickUpdater); } if (fAnnotationUpdater != null) { fProblemMarkerManager.removeListener(fAnnotationUpdater); } if (fActionBars != null) { fActionBars.dispose(); fActionBars = null; } if (fDocumentListener != null) { getDocumentProvider().getDocument(getEditorInput()).removeDocumentListener(fDocumentListener); } if (fResourceListener != null) { ResourcesPlugin.getWorkspace().removeResourceChangeListener(fResourceListener); } if (fLanguageServiceManager != null) { fLanguageServiceManager.dispose(); } fToggleBreakpointAction.dispose(); // this holds onto the IDocument fFoldingActionGroup.dispose(); if (fServiceControllerManager != null) { fServiceControllerManager.getCompletionProcessor().dispose(); fServiceControllerManager= null; } if (fParserScheduler != null) { fParserScheduler.cancel(); // avoid unnecessary work after the editor is asked to close down } fParserScheduler= null; ((StructuredSourceViewer) getSourceViewer()).setParseController(null); super.dispose(); } /** * Override creation of the normal source viewer with one that supports source folding. */ protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles) { // if (fFoldingUpdater == null) // return super.createSourceViewer(parent, ruler, styles); fAnnotationAccess= createAnnotationAccess(); fOverviewRuler= createOverviewRuler(getSharedColors()); ISourceViewer viewer= new StructuredSourceViewer(parent, ruler, getOverviewRuler(), isOverviewRulerVisible(), styles); // ensure decoration support has been created and configured. getSourceViewerDecorationSupport(viewer); if (fLanguageServiceManager != null && fLanguageServiceManager.getParseController() != null) { IMPHelp.setHelp(fLanguageServiceManager, this, viewer.getTextWidget(), IMP_EDITOR_CONTEXT); } return viewer; } protected void configureSourceViewerDecorationSupport(SourceViewerDecorationSupport support) { // X10DT Bug #546: If this input has no corresponding language descriptor, someone // can still associate the IMP editor with this file extension, in which case we // get here with no fLanguage and no fLanguageServiceManager. if (fLanguage == null || fLanguageServiceManager == null) { return; } IParseController parseController = fLanguageServiceManager.getParseController(); if (parseController == null) { return; } ILanguageSyntaxProperties syntaxProps= parseController.getSyntaxProperties(); getPreferenceStore().setValue(MATCHING_BRACKETS, true); if (syntaxProps != null) { // fBracketMatcher.setSourceVersion(getPreferenceStore().getString(JavaCore.COMPILER_SOURCE)); String[][] fences= syntaxProps.getFences(); if (fences != null) { StringBuilder sb= new StringBuilder(); for(int i= 0; i < fences.length; i++) { sb.append(fences[i][0]); sb.append(fences[i][1]); } fBracketMatcher= new DefaultCharacterPairMatcher(sb.toString().toCharArray()); support.setCharacterPairMatcher(fBracketMatcher); support.setMatchingCharacterPainterPreferenceKeys(MATCHING_BRACKETS, MATCHING_BRACKETS_COLOR); } } super.configureSourceViewerDecorationSupport(support); } /** * Jumps to the matching bracket. */ public void gotoMatchingFence() { ISourceViewer sourceViewer= getSourceViewer(); IDocument document= sourceViewer.getDocument(); if (document == null) return; IRegion selection= getSignedSelection(sourceViewer); int selectionLength= Math.abs(selection.getLength()); if (selectionLength > 1) { setStatusLineErrorMessage("Invalid selection"); sourceViewer.getTextWidget().getDisplay().beep(); return; } // #26314 int sourceCaretOffset= selection.getOffset() + selection.getLength(); if (isSurroundedByBrackets(document, sourceCaretOffset)) sourceCaretOffset -= selection.getLength(); IRegion region= fBracketMatcher.match(document, sourceCaretOffset); if (region == null) { setStatusLineErrorMessage("No matching fence!"); sourceViewer.getTextWidget().getDisplay().beep(); return; } int offset= region.getOffset(); int length= region.getLength(); if (length < 1) return; int anchor= fBracketMatcher.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) { ITextViewerExtension5 extension= (ITextViewerExtension5) sourceViewer; visible= (extension.modelOffset2WidgetOffset(targetOffset) > -1); } else { 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("Matching fence is outside the currently selected element."); sourceViewer.getTextWidget().getDisplay().beep(); return; } if (selection.getLength() < 0) targetOffset -= selection.getLength(); sourceViewer.setSelectedRange(targetOffset, selection.getLength()); sourceViewer.revealRange(targetOffset, selection.getLength()); } private boolean isBracket(char character) { ILanguageSyntaxProperties syntaxProps= fLanguageServiceManager.getParseController().getSyntaxProperties(); String[][] fences= syntaxProps.getFences(); for(int i= 0; i != fences.length; ++i) { if (fences[i][0].indexOf(character) >= 0) return true; if (fences[i][1].indexOf(character) >= 0) return true; } return false; } private boolean isSurroundedByBrackets(IDocument document, int offset) { if (offset == 0 || offset == document.getLength()) return false; try { return isBracket(document.getChar(offset - 1)) && isBracket(document.getChar(offset)); } catch (BadLocationException e) { return false; } } /** * 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 */ public IRegion getSignedSelection(ISourceViewer sourceViewer) { StyledText text= sourceViewer.getTextWidget(); 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); } public IRegion getSelectedRegion() { StyledText text= getSourceViewer().getTextWidget(); Point selection= text.getSelectionRange(); return new Region(selection.x, selection.y); } public final String PARSE_ANNOTATION = "Parse_Annotation"; private Map<IMarker, Annotation> markerParseAnnotations = new HashMap<IMarker, Annotation>(); private Map<IMarker, MarkerAnnotation> markerMarkerAnnotations = new HashMap<IMarker, MarkerAnnotation>(); /** * Refresh the marker annotations on the input document by removing any * that do not map to current parse annotations. Do this for problem * markers, specifically; ignore other types of markers. * * SMS 25 Apr 2007 */ public void refreshMarkerAnnotations(String problemMarkerType) { // Get current marker annotations IAnnotationModel model = getDocumentProvider().getAnnotationModel(getEditorInput()); Iterator annIter = model.getAnnotationIterator(); List<MarkerAnnotation> markerAnnotations = new ArrayList<MarkerAnnotation>(); while (annIter.hasNext()) { Object ann = annIter.next(); if (ann instanceof MarkerAnnotation) { markerAnnotations.add((MarkerAnnotation) ann); } } // For the current marker annotations, if any lacks a corresponding // parse annotation, delete the marker annotation from the document's // annotation model (but leave the marker on the underlying resource, // which presumably hasn't been changed, despite changes to the document) for (int i = 0; i < markerAnnotations.size(); i++) { IMarker marker = markerAnnotations.get(i).getMarker(); try { String markerType = marker.getType(); if (!markerType.endsWith(problemMarkerType)) continue; } catch (CoreException e) { // If we get a core exception here, probably something is wrong with the // marker, and we probably don't want to keep any annotation that may be // associated with it (I don't think) model.removeAnnotation(markerAnnotations.get(i)); continue; } if (markerParseAnnotations.get(marker) != null) { continue; } else { model.removeAnnotation(markerAnnotations.get(i)); } } } /** * This is a type of listener whose purpose is to monitor changes to a document * annotation model and to maintain at a mapping from markers on the underlying * resource to parse annotations on the document. * * The association of markers to annotations is determined by a subroutine that * may be more or less sophisticated in how it identifies associations. The * accuracy of the map depends on the implementation of this routine. (The * current implementation of the method simply compares text ranges of annotations * and markers.) * * The motivating purpose of the mapping is to enable the identification of marker * annotations that are (or are not) associated with a current parse annotation. * Then, for instance, marker annotations that are not associated with current parse * annotations might be removed from the document. * * No assumptions are made here about the type (or types) of marker annotation of * interest; all types of marker annotation are considered. * * SMS 25 Apr 2007 */ protected class InputAnnotationModelListener implements IAnnotationModelListener { public void modelChanged(IAnnotationModel model) { List<Annotation> currentParseAnnotations = new ArrayList<Annotation>(); List<IMarker> currentMarkers = new ArrayList<IMarker>(); markerParseAnnotations = new HashMap<IMarker,Annotation>(); markerMarkerAnnotations = new HashMap<IMarker,MarkerAnnotation>(); // Collect the current set of markers and parse annotations; // also maintain a map of markers to marker annotations (as // there doesn't seem to be a way to get from a marker to the // annotations that may represent it) Iterator annotations = model.getAnnotationIterator(); while (annotations.hasNext()) { Object ann = annotations.next(); if (ann instanceof MarkerAnnotation) { IMarker marker = ((MarkerAnnotation)ann).getMarker(); if (marker.exists()) { currentMarkers.add(marker); } markerMarkerAnnotations.put(marker, (MarkerAnnotation) ann); } else if (ann instanceof Annotation) { Annotation annotation = (Annotation) ann; if (annotation.getType().equals(PARSE_ANNOTATION_TYPE)) { currentParseAnnotations.add(annotation); } } } // Create a mapping between current markers and parse annotations for (int i = 0; i < currentMarkers.size(); i++) { IMarker marker = currentMarkers.get(i); Annotation annotation = findParseAnnotationForMarker(model, marker, currentParseAnnotations); if (annotation != null) { markerParseAnnotations.put(marker, annotation); } } } public Annotation findParseAnnotationForMarker(IAnnotationModel model, IMarker marker, List parseAnnotations) { Integer markerStartAttr = null; Integer markerEndAttr = null; try { // SMS 22 May 2007: With markers created through the editor the CHAR_START // and CHAR_END attributes are null, giving rise to NPEs here. Not sure // why this happens, but it seems to help down the line to trap the NPE. markerStartAttr = ((Integer) marker.getAttribute(IMarker.CHAR_START)); markerEndAttr = ((Integer) marker.getAttribute(IMarker.CHAR_END)); if (markerStartAttr == null || markerEndAttr == null) { return null; } } catch (CoreException e) { // RMF 7/25/2008 -- This exception should never occur, now that we check that each marker still exists in modelChanged() // (see Bug 242098). RuntimePlugin.getInstance().logException("UniversalEditor.findParseAnnotationForMarker: CoreException getting marker start and end attributes", e); return null; } catch (NullPointerException e) { RuntimePlugin.getInstance().logException("UniversalEditor.findParseAnnotationForMarker: NullPointerException getting marker start and end attributes", e); return null; } int markerStart = markerStartAttr.intValue(); int markerEnd = markerEndAttr.intValue(); int markerLength = markerEnd - markerStart; for (int j = 0; j < parseAnnotations.size(); j++) { Annotation parseAnnotation = (Annotation) parseAnnotations.get(j); Position pos = model.getPosition(parseAnnotation); if (pos == null) // And this would be why? continue; int annotationStart = pos.offset; int annotationLength = pos.length; //System.out.println("\tfindParseAnnotationForMarker: Checking annotation offset and length = " + annotationStart + ", " + annotationLength); if (markerStart == annotationStart && markerLength == annotationLength) { //System.out.println("\tfindParseAnnotationForMarker: Returning annotation at offset = " + markerStart); return parseAnnotation; } else { //System.out.println("\tfindParseAnnotationForMarker: Not returning annotation at offset = " + markerStart); } } //System.out.println(" findParseAnnotationForMarker: No corresponding annotation found; returning null"); return null; } } protected void doSetInput(IEditorInput input) throws CoreException { // SMS 22 May 2007: added try/catch around doSetInput(..) try { super.doSetInput(input); } catch (NullPointerException e) { return; } setInsertMode(SMART_INSERT); // SMS 25 Apr 2007 // Added for maintenance of associations between marker annotations // and parse annotations IAnnotationModel annotationModel = getDocumentProvider().getAnnotationModel(input); // RMF 6 Jun 2007 - Not sure why annotationModel is null for files outside the // workspace, but they are, so make sure we don't cause an NPE here. if (annotationModel != null) annotationModel.addAnnotationModelListener(new InputAnnotationModelListener()); } /** * Add a Model listener to this editor. Any time the underlying AST is recomputed, the listener is notified. * * @param listener the listener to notify of Model changes */ public void addModelListener(IModelListener listener) { fParserScheduler.addModelListener(listener); } /** * Remove a Model listener from this editor. * * @param listener the listener to remove */ public void removeModelListener(IModelListener listener) { fParserScheduler.removeModelListener(listener); } class StructuredSourceViewerConfiguration extends TextSourceViewerConfiguration { public StructuredSourceViewerConfiguration(IPreferenceStore prefStore) { super(prefStore); } public int getTabWidth(ISourceViewer sourceViewer) { boolean langSpecificSetting= fLangSpecificPrefs != null && fLangSpecificPrefs.isDefined(PreferenceConstants.P_TAB_WIDTH); return langSpecificSetting ? fLangSpecificPrefs.getIntPreference(PreferenceConstants.P_TAB_WIDTH) : PreferenceCache.tabWidth; } public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) { if (fServiceControllerManager == null || fLanguageServiceManager.getTokenColorer() == null) { return super.getPresentationReconciler(sourceViewer); } // BUG Perhaps we shouldn't use a PresentationReconciler; its JavaDoc says it runs in the UI thread! PresentationReconciler reconciler= new PresentationReconciler(); reconciler.setRepairer(new PresentationRepairer(), IDocument.DEFAULT_CONTENT_TYPE); reconciler.setDamager(new PresentationDamager(), IDocument.DEFAULT_CONTENT_TYPE); return reconciler; } public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) { if (fServiceControllerManager == null) { return super.getContentAssistant(sourceViewer); } ContentAssistant ca= new ContentAssistant(); ca.setContentAssistProcessor(fServiceControllerManager.getCompletionProcessor(), IDocument.DEFAULT_CONTENT_TYPE); ca.setInformationControlCreator(getInformationControlCreator(sourceViewer)); return ca; } public IAnnotationHover getAnnotationHover(ISourceViewer sourceViewer) { if (fLanguageServiceManager == null) { return super.getAnnotationHover(sourceViewer); } IAnnotationHover hover= fLanguageServiceManager.getAnnotationHover(); if (hover == null) hover= new DefaultAnnotationHover(); return hover; } public IAutoEditStrategy[] getAutoEditStrategies(ISourceViewer sourceViewer, String contentType) { if (fLanguageServiceManager == null) { return super.getAutoEditStrategies(sourceViewer, contentType); } Set<org.eclipse.imp.services.IAutoEditStrategy> autoEdits= fLanguageServiceManager.getAutoEditStrategies(); if (autoEdits == null || autoEdits.size() == 0) { return super.getAutoEditStrategies(sourceViewer, contentType); } return autoEdits.toArray(new IAutoEditStrategy[autoEdits.size()]); } public IContentFormatter getContentFormatter(ISourceViewer sourceViewer) { // Disable the content formatter if no language-specific implementation exists. // N.B.: This will probably always be null, since this method gets called before // the formatting controller has been instantiated (which happens in // instantiateServiceControllers()). if (fServiceControllerManager == null || fServiceControllerManager.getFormattingController() == null) return null; // For now, assumes only one content type (i.e. one kind of partition) ContentFormatter formatter= new ContentFormatter(); formatter.setFormattingStrategy(fServiceControllerManager.getFormattingController(), IDocument.DEFAULT_CONTENT_TYPE); return formatter; } public String[] getDefaultPrefixes(ISourceViewer sourceViewer, String contentType) { return super.getDefaultPrefixes(sourceViewer, contentType); } public ITextDoubleClickStrategy getDoubleClickStrategy(ISourceViewer sourceViewer, String contentType) { return super.getDoubleClickStrategy(sourceViewer, contentType); } public IHyperlinkDetector[] getHyperlinkDetectors(ISourceViewer sourceViewer) { if (fServiceControllerManager != null && fServiceControllerManager.getHyperLinkController() != null) return new IHyperlinkDetector[] { fServiceControllerManager.getHyperLinkController() }; return super.getHyperlinkDetectors(sourceViewer); } public IHyperlinkPresenter getHyperlinkPresenter(ISourceViewer sourceViewer) { return super.getHyperlinkPresenter(sourceViewer); } public String[] getIndentPrefixes(ISourceViewer sourceViewer, String contentType) { return super.getIndentPrefixes(sourceViewer, contentType); } public IInformationControlCreator getInformationControlCreator(ISourceViewer sourceViewer) { return new IInformationControlCreator() { public IInformationControl createInformationControl(Shell parent) { // int shellStyle= SWT.RESIZE | SWT.TOOL; // int style= SWT.NONE; // SWT.V_SCROLL | SWT.H_SCROLL; // if (BrowserInformationControl.isAvailable(parent)) // return new BrowserInformationControl(parent, SWT.TOOL | SWT.NO_TRIM, SWT.NONE, EditorsUI.getTooltipAffordanceString()); // else // return new OutlineInformationControl(parent, shellStyle, style, new HTMLTextPresenter(false)); return new DefaultInformationControl(parent, "Press 'F2' for focus", new HTMLTextPresenter(true)); } }; } private InformationPresenter fInfoPresenter; public IInformationPresenter getInformationPresenter(ISourceViewer sourceViewer) { if (fLanguageServiceManager == null) { return super.getInformationPresenter(sourceViewer); } if (fInfoPresenter == null) { fInfoPresenter= new InformationPresenter(getInformationControlCreator(sourceViewer)); fInfoPresenter.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer)); fInfoPresenter.setAnchor(AbstractInformationControlManager.ANCHOR_GLOBAL); IInformationProvider provider= new IInformationProvider() { // this should be language-specific public IRegion getSubject(ITextViewer textViewer, int offset) { IParseController pc= fLanguageServiceManager.getParseController(); ISourcePositionLocator locator= pc.getSourcePositionLocator(); if (locator == null) { return new Region(offset, 0); } Object selNode= locator.findNode(pc, offset); return new Region(locator.getStartOffset(selNode), locator.getLength(selNode)); } public String getInformation(ITextViewer textViewer, IRegion subject) { IParseController pc= fLanguageServiceManager.getParseController(); ISourcePositionLocator locator= pc.getSourcePositionLocator(); if (locator == null) { return ""; } IDocumentationProvider docProvider= fLanguageServiceManager.getDocProvider(); Object selNode= locator.findNode(pc.getCurrentAst(), subject.getOffset()); return (docProvider != null) ? docProvider.getDocumentation(selNode, pc) : "No documentation available on the selected entity."; } }; fInfoPresenter.setInformationProvider(provider, IDocument.DEFAULT_CONTENT_TYPE); fInfoPresenter.setSizeConstraints(60, 10, true, false); fInfoPresenter.setRestoreInformationControlBounds(getSettings("outline_presenter_bounds"), true, true); //$NON-NLS-1$ } return fInfoPresenter; } public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType) { if (fServiceControllerManager == null) { return super.getTextHover(sourceViewer, contentType); } return fServiceControllerManager.getHoverHelpController(); } public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType, int stateMask) { return super.getTextHover(sourceViewer, contentType, stateMask); } public IUndoManager getUndoManager(ISourceViewer sourceViewer) { return super.getUndoManager(sourceViewer); } public IAnnotationHover getOverviewRulerAnnotationHover(ISourceViewer sourceViewer) { return super.getOverviewRulerAnnotationHover(sourceViewer); } private class OutlineInformationProvider implements IInformationProvider, IInformationProviderExtension { private TreeModelBuilderBase fBuilder; public IRegion getSubject(ITextViewer textViewer, int offset) { return new Region(offset, 0); // Could be anything, since it's ignored below in getInformation2()... } public String getInformation(ITextViewer textViewer, IRegion subject) { return "never called?!?"; // shouldn't be called, given IInformationProviderExtension??? } public Object getInformation2(ITextViewer textViewer, IRegion subject) { if (fBuilder == null) { fBuilder= fLanguageServiceManager.getModelBuilder(); } return fBuilder.buildTree(fLanguageServiceManager.getParseController().getCurrentAst()); } } private IInformationProvider fOutlineElementProvider= new OutlineInformationProvider(); public IInformationPresenter getOutlinePresenter(ISourceViewer sourceViewer) { if (fLanguageServiceManager == null) { return null; } if (fLanguageServiceManager.getModelBuilder() == null) { return null; } InformationPresenter presenter; presenter= new InformationPresenter(getOutlinePresenterControlCreator(sourceViewer, IEditorActionDefinitionIds.SHOW_OUTLINE)); presenter.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer)); presenter.setAnchor(AbstractInformationControlManager.ANCHOR_GLOBAL); IInformationProvider provider= fOutlineElementProvider; presenter.setInformationProvider(provider, IDocument.DEFAULT_CONTENT_TYPE); // TODO Should associate all other partition types with this provider, too presenter.setSizeConstraints(50, 20, true, false); presenter.setRestoreInformationControlBounds(getSettings("outline_presenter_bounds"), true, true); //$NON-NLS-1$ return presenter; } /** * Returns the outline presenter control creator. The creator is a factory creating outline presenter controls for the given source viewer. This * implementation always returns a creator for <code>OutlineInformationControl</code> instances. * * @param sourceViewer the source viewer to be configured by this configuration * @param commandId the ID of the command that opens this control * @return an information control creator */ private IInformationControlCreator getOutlinePresenterControlCreator(ISourceViewer sourceViewer, final String commandId) { return new IInformationControlCreator() { public IInformationControl createInformationControl(Shell parent) { int shellStyle= SWT.RESIZE; int treeStyle= SWT.V_SCROLL | SWT.H_SCROLL; return new OutlineInformationControl(parent, shellStyle, treeStyle, commandId, UniversalEditor.this.fLanguage); } }; } /** * Returns the hierarchy presenter which will determine and shown type hierarchy information requested for the current cursor position. * * @param sourceViewer the source viewer to be configured by this configuration * @param doCodeResolve a boolean which specifies whether code resolve should be used to compute the program element * @return an information presenter */ public IInformationPresenter getHierarchyPresenter(ISourceViewer sourceViewer, boolean doCodeResolve) { InformationPresenter presenter= new InformationPresenter(getHierarchyPresenterControlCreator(sourceViewer)); presenter.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer)); presenter.setAnchor(AbstractInformationControlManager.ANCHOR_GLOBAL); IInformationProvider provider= null; // TODO RMF new HierarchyInformationProvider(this); presenter.setInformationProvider(provider, IDocument.DEFAULT_CONTENT_TYPE); // presenter.setInformationProvider(provider, IJavaPartitions.JAVA_DOC); // presenter.setInformationProvider(provider, IJavaPartitions.JAVA_MULTI_LINE_COMMENT); // presenter.setInformationProvider(provider, IJavaPartitions.JAVA_SINGLE_LINE_COMMENT); // presenter.setInformationProvider(provider, IJavaPartitions.JAVA_STRING); // presenter.setInformationProvider(provider, IJavaPartitions.JAVA_CHARACTER); presenter.setSizeConstraints(50, 20, true, false); presenter.setRestoreInformationControlBounds(getSettings("hierarchy_presenter_bounds"), true, true); //$NON-NLS-1$ return presenter; } private IInformationControlCreator getHierarchyPresenterControlCreator(ISourceViewer sourceViewer) { return new IInformationControlCreator() { public IInformationControl createInformationControl(Shell parent) { // int shellStyle= SWT.RESIZE; // int treeStyle= SWT.V_SCROLL | SWT.H_SCROLL; return new DefaultInformationControl(parent); // HierarchyInformationControl(parent, shellStyle, treeStyle); } }; } /** * Returns the settings for the given section. * * @param sectionName the section name * @return the settings * @since 3.0 */ private IDialogSettings getSettings(String sectionName) { IDialogSettings settings= RuntimePlugin.getInstance().getDialogSettings().getSection(sectionName); if (settings == null) settings= RuntimePlugin.getInstance().getDialogSettings().addNewSection(sectionName); return settings; } } private class PresentationDamager implements IPresentationDamager { public IRegion getDamageRegion(ITypedRegion partition, DocumentEvent event, boolean documentPartitioningChanged) { // Ask the language's token colorer how much of the document presentation needs to be recomputed. final ITokenColorer tokenColorer= fLanguageServiceManager.getTokenColorer(); if (tokenColorer != null) return tokenColorer.calculateDamageExtent(partition, fLanguageServiceManager.getParseController()); else return partition; } public void setDocument(IDocument document) {} } private class PresentationRepairer implements IPresentationRepairer { // For checking whether the damage region has changed ITypedRegion previousDamage= null; private final IProgressMonitor fProgressMonitor= new NullProgressMonitor(); public void createPresentation(TextPresentation presentation, ITypedRegion damage) { boolean hyperlinkRestore= false; // try { // throw new Exception(); // } catch (Exception e) { // System.out.println("Entered PresentationRepairer.createPresentation()"); // e.printStackTrace(System.out); // } // If the given damage region is the same as the previous one, assume it's due to removing a hyperlink decoration if (previousDamage != null && damage.getOffset() == previousDamage.getOffset() && damage.getLength() == previousDamage.getLength()) { hyperlinkRestore= true; } // BUG Should we really just ignore the presentation passed in??? // JavaDoc says we're responsible for "merging" our changes in... try { if (fServiceControllerManager.getPresentationController() != null) { // System.out.println("Scheduling repair for damage to region " + damage.getOffset() + ":" + damage.getLength() + " in doc of length " + fDocument.getLength()); fServiceControllerManager.getPresentationController().damage(damage); if (hyperlinkRestore) { // System.out.println("** Forcing repair for hyperlink damage to region " + damage.getOffset() + ":" + damage.getLength() + " in doc of length " + getDocumentProvider().getDocument(getEditorInput()).getLength()); fServiceControllerManager.getPresentationController().update(fLanguageServiceManager.getParseController(), fProgressMonitor); } } } catch (Exception e) { ErrorHandler.reportError("Could not repair damage ", e); } previousDamage= damage; } public void setDocument(IDocument document) { } } private IMessageHandler fAnnotationCreator= new AnnotationCreator(this, PARSE_ANNOTATION_TYPE); private final IRegionSelectionService fRegionSelector= new IRegionSelectionService() { public void selectAndReveal(int startOffset, int length) { IEditorPart activeEditor= PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor(); AbstractTextEditor textEditor= (AbstractTextEditor) activeEditor; textEditor.selectAndReveal(startOffset, length); } }; private EditorErrorTickUpdater fEditorErrorTickUpdater; private IProblemChangedListener fAnnotationUpdater; private class AnnotationCreatorListener implements IModelListener { public AnalysisRequired getAnalysisRequired() { return AnalysisRequired.NONE; // Even if it doesn't scan, it's ok - this posts the error annotations! } public void update(IParseController parseController, IProgressMonitor monitor) { // SMS 25 Apr 2007 // Since parsing has finished, check whether the marker annotations // are up-to-date with the most recent parse annotations. // Assuming that's often enough--i.e., don't refresh the marker // annotations after every update to the document annotation model // since there will be many of these, including possibly many that // don't relate to problem markers. final IAnnotationTypeInfo annotationTypeInfo= parseController.getAnnotationTypeInfo(); if (annotationTypeInfo != null) { List problemMarkerTypes = annotationTypeInfo.getProblemMarkerTypes(); for (int i = 0; i < problemMarkerTypes.size(); i++) { refreshMarkerAnnotations((String)problemMarkerTypes.get(i)); } } } } public String getSelectionText() { Point sel= getSelection(); IFileEditorInput fileEditorInput= (IFileEditorInput) getEditorInput(); IDocument document= getDocumentProvider().getDocument(fileEditorInput); try { return document.get(sel.x, sel.y); } catch (BadLocationException e) { e.printStackTrace(); return ""; } } public Point getSelection() { ISelection sel= this.getSelectionProvider().getSelection(); ITextSelection textSel= (ITextSelection) sel; return new Point(textSel.getOffset(), textSel.getLength()); } public boolean canPerformFind() { return true; } public IParseController getParseController() { return fLanguageServiceManager.getParseController(); } public IOccurrenceMarker getOccurrenceMarker() { return fLanguageServiceManager.getOccurrenceMarker(); } // SMS 4 May 2006: // Added this as the only way I could think of (so far) to // remove parser annotations that I expect to be duplicated // if a save triggers a build that leads to the creation // of markers and another set of annotations. public void doSave(IProgressMonitor progressMonitor) { // SMS 25 Apr 2007: Removing parser annotations here // may not hurt but also doesn't seem to be necessary //removeParserAnnotations(); super.doSave(progressMonitor); } public void removeParserAnnotations() { IAnnotationModel model= getDocumentProvider().getAnnotationModel(getEditorInput()); for(Iterator i= model.getAnnotationIterator(); i.hasNext(); ) { Annotation a= (Annotation) i.next(); if (a.getType().equals(PARSE_ANNOTATION_TYPE)) model.removeAnnotation(a); } } public String toString() { String langName= (fLanguage != null ? " for " + fLanguage.getName() : ""); String inputDesc= (fParserScheduler != null && fLanguageServiceManager.getParseController() != null && fLanguageServiceManager.getParseController().getPath() != null) ? "source " + fLanguageServiceManager.getParseController().getPath().toPortableString() : ""; return "Universal Editor" + langName + " on " + inputDesc + getEditorInput(); } }