/* * Copyright (c) 2012, the Dart project authors. * * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.google.dart.tools.ui.internal.text.editor; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.dart.engine.ast.AstNode; import com.google.dart.engine.ast.ClassDeclaration; import com.google.dart.engine.ast.ClassMember; import com.google.dart.engine.ast.CompilationUnit; import com.google.dart.engine.ast.CompilationUnitMember; import com.google.dart.engine.ast.NodeList; import com.google.dart.engine.ast.visitor.NodeLocator; import com.google.dart.engine.context.AnalysisContext; import com.google.dart.engine.element.CompilationUnitElement; import com.google.dart.engine.index.Index; import com.google.dart.engine.sdk.DartSdk; import com.google.dart.engine.sdk.DirectoryBasedDartSdk; import com.google.dart.engine.search.SearchEngine; import com.google.dart.engine.search.SearchEngineFactory; import com.google.dart.engine.services.assist.AssistContext; import com.google.dart.engine.source.FileBasedSource; import com.google.dart.engine.source.Source; import com.google.dart.engine.utilities.source.SourceRange; import com.google.dart.tools.core.DartCore; import com.google.dart.tools.core.DartCoreDebug; import com.google.dart.tools.core.MessageConsole; import com.google.dart.tools.core.analysis.model.AnalysisServerData; import com.google.dart.tools.core.analysis.model.AnalysisServerNavigationListener; import com.google.dart.tools.core.analysis.model.AnalysisServerOutlineListener; import com.google.dart.tools.core.analysis.model.Project; import com.google.dart.tools.core.analysis.model.ProjectManager; import com.google.dart.tools.core.formatter.DefaultCodeFormatterConstants; import com.google.dart.tools.core.model.DartModelException; import com.google.dart.tools.core.model.SourceReference; import com.google.dart.tools.core.pub.PubCacheManager_NEW; import com.google.dart.tools.core.utilities.general.SourceRangeFactory; import com.google.dart.tools.internal.corext.refactoring.util.ExecutionUtils; import com.google.dart.tools.internal.corext.refactoring.util.RunnableEx; import com.google.dart.tools.internal.search.ui.DartSearchActionGroup_NEW; import com.google.dart.tools.internal.search.ui.DartSearchActionGroup_OLD; import com.google.dart.tools.ui.DartToolsPlugin; import com.google.dart.tools.ui.DartX; import com.google.dart.tools.ui.IContextMenuConstants; import com.google.dart.tools.ui.PreferenceConstants; import com.google.dart.tools.ui.actions.DartEditorActionDefinitionIds; import com.google.dart.tools.ui.actions.DartdocActionGroup_OLD; import com.google.dart.tools.ui.actions.OpenEditorActionGroup_NEW; import com.google.dart.tools.ui.actions.OpenEditorActionGroup_OLD; import com.google.dart.tools.ui.actions.OpenViewActionGroup_NEW; import com.google.dart.tools.ui.actions.OpenViewActionGroup_OLD; import com.google.dart.tools.ui.actions.RefactorActionGroup_NEW; import com.google.dart.tools.ui.actions.ShowSelectionLabelAction; import com.google.dart.tools.ui.instrumentation.UIInstrumentation; import com.google.dart.tools.ui.instrumentation.UIInstrumentationBuilder; import com.google.dart.tools.ui.internal.actions.ActionUtil; import com.google.dart.tools.ui.internal.actions.FoldingActionGroup; import com.google.dart.tools.ui.internal.formatter.DartFormatter; import com.google.dart.tools.ui.internal.formatter.DartFormatter.FormattedSource; import com.google.dart.tools.ui.internal.text.DartHelpContextIds; import com.google.dart.tools.ui.internal.text.IProductConstants; import com.google.dart.tools.ui.internal.text.ProductProperties; import com.google.dart.tools.ui.internal.text.dart.DartPriorityFileEditor; import com.google.dart.tools.ui.internal.text.dart.DartReconcilingEditor; import com.google.dart.tools.ui.internal.text.dart.DartReconcilingStrategy; import com.google.dart.tools.ui.internal.text.dart.hover.SourceViewerInformationControl; import com.google.dart.tools.ui.internal.text.editor.saveactions.RemoveTrailingWhitespaceAction; import com.google.dart.tools.ui.internal.text.editor.selectionactions.GoToNextPreviousMemberAction; import com.google.dart.tools.ui.internal.text.editor.selectionactions.SelectionHistory; import com.google.dart.tools.ui.internal.text.editor.selectionactions.StructureSelectEnclosingAction; import com.google.dart.tools.ui.internal.text.editor.selectionactions.StructureSelectHistoryAction; import com.google.dart.tools.ui.internal.text.editor.selectionactions.StructureSelectNextAction; import com.google.dart.tools.ui.internal.text.editor.selectionactions.StructureSelectPreviousAction; import com.google.dart.tools.ui.internal.text.editor.selectionactions.StructureSelectionAction; import com.google.dart.tools.ui.internal.text.functions.DartChangeHover; import com.google.dart.tools.ui.internal.text.functions.DartPairMatcher; import com.google.dart.tools.ui.internal.text.functions.DartWordIterator; import com.google.dart.tools.ui.internal.text.functions.DocumentCharacterIterator; import com.google.dart.tools.ui.internal.text.functions.PreferencesAdapter; import com.google.dart.tools.ui.internal.util.DartUIHelp; import com.google.dart.tools.ui.internal.viewsupport.IViewPartInputProvider; import com.google.dart.tools.ui.text.DartPartitions; import com.google.dart.tools.ui.text.DartSourceViewerConfiguration; import com.google.dart.tools.ui.text.DartTextTools; import com.google.dart.tools.ui.text.folding.IDartFoldingStructureProvider; import com.google.dart.tools.ui.text.folding.IDartFoldingStructureProviderExtension; import com.ibm.icu.text.BreakIterator; import org.dartlang.analysis.server.protocol.NavigationRegion; import org.dartlang.analysis.server.protocol.Outline; import org.eclipse.core.commands.operations.IOperationApprover; import org.eclipse.core.commands.operations.IUndoContext; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ProjectScope; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.ListenerList; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.IScopeContext; import org.eclipse.help.IContextProvider; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IInformationControl; import org.eclipse.jface.text.IInformationControlCreator; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextHover; import org.eclipse.jface.text.ITextOperationTarget; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.ITextViewerExtension2; import org.eclipse.jface.text.ITextViewerExtension5; import org.eclipse.jface.text.MarkSelection; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.TextSelection; import org.eclipse.jface.text.TextUtilities; import org.eclipse.jface.text.link.LinkedModeModel; import org.eclipse.jface.text.link.LinkedPosition; import org.eclipse.jface.text.reconciler.IReconciler; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.CompositeRuler; import org.eclipse.jface.text.source.IAnnotationModel; import org.eclipse.jface.text.source.ICharacterPairMatcher; import org.eclipse.jface.text.source.IOverviewRuler; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.IVerticalRuler; import org.eclipse.jface.text.source.IVerticalRulerColumn; import org.eclipse.jface.text.source.LineChangeHover; import org.eclipse.jface.text.source.SourceViewerConfiguration; 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.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.SelectionChangedEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CaretEvent; import org.eclipse.swt.custom.CaretListener; import org.eclipse.swt.custom.ST; 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.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.IPageLayout; import org.eclipse.ui.IPartListener2; import org.eclipse.ui.IPartService; import org.eclipse.ui.IURIEditorInput; import org.eclipse.ui.IWorkbenchActionConstants; 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.DefaultEncodingSupport; import org.eclipse.ui.editors.text.EditorsUI; import org.eclipse.ui.editors.text.IEncodingSupport; import org.eclipse.ui.operations.NonLocalUndoUserApprover; import org.eclipse.ui.part.IShowInSource; import org.eclipse.ui.part.IShowInTargetList; import org.eclipse.ui.part.ShowInContext; import org.eclipse.ui.texteditor.AbstractDecoratedTextEditor; import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants; import org.eclipse.ui.texteditor.AnnotationPreference; import org.eclipse.ui.texteditor.ChainedPreferenceStore; import org.eclipse.ui.texteditor.IEditorStatusLine; import org.eclipse.ui.texteditor.ITextEditorActionConstants; import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; import org.eclipse.ui.texteditor.IUpdate; import org.eclipse.ui.texteditor.SourceViewerDecorationSupport; import org.eclipse.ui.texteditor.TextNavigationAction; import org.eclipse.ui.texteditor.TextOperationAction; import org.eclipse.ui.views.contentoutline.ContentOutline; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; import org.osgi.service.prefs.BackingStoreException; import java.io.File; import java.lang.reflect.InvocationTargetException; import java.net.URI; import java.text.CharacterIterator; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; /** * Dart specific text editor. */ @SuppressWarnings({"unused", "deprecation"}) public abstract class DartEditor extends AbstractDecoratedTextEditor implements IViewPartInputProvider, DartReconcilingEditor, DartPriorityFileEditor { /** * Adapts an options {@link IEclipsePreferences} to * {@link org.eclipse.jface.preference.IPreferenceStore}. * <p> * This preference store is read-only i.e. write access throws an * {@link java.lang.UnsupportedOperationException}. * </p> */ public static class EclipsePreferencesAdapter implements IPreferenceStore { /** * Preference change listener. Listens for events preferences fires a * {@link org.eclipse.jface.util.PropertyChangeEvent} on this adapter with arguments from the * received event. */ private class PreferenceChangeListener implements IEclipsePreferences.IPreferenceChangeListener { /** * {@inheritDoc} */ @Override public void preferenceChange(final IEclipsePreferences.PreferenceChangeEvent event) { if (Display.getCurrent() == null) { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { firePropertyChangeEvent(event.getKey(), event.getOldValue(), event.getNewValue()); } }); } else { firePropertyChangeEvent(event.getKey(), event.getOldValue(), event.getNewValue()); } } } /** Listeners on on this adapter */ private final ListenerList fListeners = new ListenerList(ListenerList.IDENTITY); /** Listener on the node */ private final IEclipsePreferences.IPreferenceChangeListener fListener = new PreferenceChangeListener(); /** wrapped node */ private final IScopeContext fContext; private final String fQualifier; /** * Initialize with the node to wrap * * @param context the context to access * @param qualifier the qualifier */ public EclipsePreferencesAdapter(IScopeContext context, String qualifier) { fContext = context; fQualifier = qualifier; } /** * {@inheritDoc} */ @Override public void addPropertyChangeListener(IPropertyChangeListener listener) { if (fListeners.size() == 0) { getNode().addPreferenceChangeListener(fListener); } fListeners.add(listener); } /** * {@inheritDoc} */ @Override public boolean contains(String name) { return getNode().get(name, null) != null; } /** * {@inheritDoc} */ @Override public void firePropertyChangeEvent(String name, Object oldValue, Object newValue) { PropertyChangeEvent event = new PropertyChangeEvent(this, name, oldValue, newValue); Object[] listeners = fListeners.getListeners(); for (int i = 0; i < listeners.length; i++) { ((IPropertyChangeListener) listeners[i]).propertyChange(event); } } /** * {@inheritDoc} */ @Override public boolean getBoolean(String name) { return getNode().getBoolean(name, BOOLEAN_DEFAULT_DEFAULT); } /** * {@inheritDoc} */ @Override public boolean getDefaultBoolean(String name) { return BOOLEAN_DEFAULT_DEFAULT; } /** * {@inheritDoc} */ @Override public double getDefaultDouble(String name) { return DOUBLE_DEFAULT_DEFAULT; } /** * {@inheritDoc} */ @Override public float getDefaultFloat(String name) { return FLOAT_DEFAULT_DEFAULT; } /** * {@inheritDoc} */ @Override public int getDefaultInt(String name) { return INT_DEFAULT_DEFAULT; } /** * {@inheritDoc} */ @Override public long getDefaultLong(String name) { return LONG_DEFAULT_DEFAULT; } /** * {@inheritDoc} */ @Override public String getDefaultString(String name) { return STRING_DEFAULT_DEFAULT; } /** * {@inheritDoc} */ @Override public double getDouble(String name) { return getNode().getDouble(name, DOUBLE_DEFAULT_DEFAULT); } /** * {@inheritDoc} */ @Override public float getFloat(String name) { return getNode().getFloat(name, FLOAT_DEFAULT_DEFAULT); } /** * {@inheritDoc} */ @Override public int getInt(String name) { return getNode().getInt(name, INT_DEFAULT_DEFAULT); } /** * {@inheritDoc} */ @Override public long getLong(String name) { return getNode().getLong(name, LONG_DEFAULT_DEFAULT); } /** * {@inheritDoc} */ @Override public String getString(String name) { return getNode().get(name, STRING_DEFAULT_DEFAULT); } /** * {@inheritDoc} */ @Override public boolean isDefault(String name) { return false; } /** * {@inheritDoc} */ @Override public boolean needsSaving() { try { return getNode().keys().length > 0; } catch (BackingStoreException e) { // ignore } return true; } /** * {@inheritDoc} */ @Override public void putValue(String name, String value) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ @Override public void removePropertyChangeListener(IPropertyChangeListener listener) { fListeners.remove(listener); if (fListeners.size() == 0) { getNode().removePreferenceChangeListener(fListener); } } /** * {@inheritDoc} */ @Override public void setDefault(String name, boolean value) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ @Override public void setDefault(String name, double value) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ @Override public void setDefault(String name, float value) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ @Override public void setDefault(String name, int value) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ @Override public void setDefault(String name, long value) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ @Override public void setDefault(String name, String defaultObject) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ @Override public void setToDefault(String name) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ @Override public void setValue(String name, boolean value) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ @Override public void setValue(String name, double value) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ @Override public void setValue(String name, float value) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ @Override public void setValue(String name, int value) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ @Override public void setValue(String name, long value) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ @Override public void setValue(String name, String value) { throw new UnsupportedOperationException(); } protected IEclipsePreferences getNode() { return fContext.getNode(fQualifier); } } /** * Internal implementation class for a change listener. */ 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(ISelectionProvider selectionProvider) { if (selectionProvider == null) { return; } if (selectionProvider instanceof IPostSelectionProvider) { 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(ISelectionProvider selectionProvider) { if (selectionProvider == null) { return; } if (selectionProvider instanceof IPostSelectionProvider) { IPostSelectionProvider provider = (IPostSelectionProvider) selectionProvider; provider.removePostSelectionChangedListener(this); } else { selectionProvider.removeSelectionChangedListener(this); } } } /** * Text operation action to delete the next sub-word. */ protected class DeleteNextSubWordAction extends NextSubWordAction implements IUpdate { /** * Creates a new delete next sub-word action. */ public DeleteNextSubWordAction() { super(ST.DELETE_WORD_NEXT); } @Override public void update() { setEnabled(isEditorInputModifiable()); } @Override protected void setCaretPosition(final int position) { if (!validateEditorInputState()) { return; } final ISourceViewer viewer = getSourceViewer(); StyledText text = viewer.getTextWidget(); Point widgetSelection = text.getSelection(); if (isBlockSelectionModeEnabled() && widgetSelection.y != widgetSelection.x) { final int caret = text.getCaretOffset(); final int offset = modelOffset2WidgetOffset(viewer, position); if (caret == widgetSelection.x) { text.setSelectionRange(widgetSelection.y, offset - widgetSelection.y); } else { text.setSelectionRange(widgetSelection.x, offset - widgetSelection.x); } text.invokeAction(ST.DELETE_NEXT); } else { Point selection = viewer.getSelectedRange(); final int caret, length; if (selection.y != 0) { caret = selection.x; length = selection.y; } else { caret = widgetOffset2ModelOffset(viewer, text.getCaretOffset()); length = position - caret; } try { viewer.getDocument().replace(caret, length, ""); //$NON-NLS-1$ } catch (BadLocationException exception) { // Should not happen } } } } /** * Text operation action to delete the previous sub-word. */ protected class DeletePreviousSubWordAction extends PreviousSubWordAction implements IUpdate { /** * Creates a new delete previous sub-word action. */ public DeletePreviousSubWordAction() { super(ST.DELETE_WORD_PREVIOUS); } @Override public void update() { setEnabled(isEditorInputModifiable()); } @Override protected void setCaretPosition(int position) { if (!validateEditorInputState()) { return; } final int length; final ISourceViewer viewer = getSourceViewer(); StyledText text = viewer.getTextWidget(); Point widgetSelection = text.getSelection(); if (isBlockSelectionModeEnabled() && widgetSelection.y != widgetSelection.x) { final int caret = text.getCaretOffset(); final int offset = modelOffset2WidgetOffset(viewer, position); if (caret == widgetSelection.x) { text.setSelectionRange(widgetSelection.y, offset - widgetSelection.y); } else { text.setSelectionRange(widgetSelection.x, offset - widgetSelection.x); } text.invokeAction(ST.DELETE_PREVIOUS); } else { Point selection = viewer.getSelectedRange(); if (selection.y != 0) { position = selection.x; length = selection.y; } else { length = widgetOffset2ModelOffset(viewer, text.getCaretOffset()) - position; } try { viewer.getDocument().replace(position, length, ""); //$NON-NLS-1$ } catch (BadLocationException exception) { // Should not happen } } } } /** * Format element action to format the enclosing Dart element. * <p> * The format element action works as follows: * <ul> * <li>If there is no selection and the caret is positioned on a Dart element, only this element * is formatted. If the element has some accompanying comment, then the comment is formatted as * well.</li> * <li>If the selection spans one or more partitions of the document, then all partitions covered * by the selection are entirely formatted.</li> * <p> * Partitions at the end of the selection are not completed, except for comments. */ protected class FormatElementAction extends Action implements IUpdate { private class FormatElementJob extends Job { // For debugging. private final boolean PRINT_TO_CONSOLE = false; private final IFile file; FormatElementJob(IFile file) { super("Formatting " + file.getLocation()); this.file = file; } @Override protected IStatus run(IProgressMonitor monitor) { MessageConsole console = DartCore.getConsole(); if (PRINT_TO_CONSOLE) { console.clear(); console.println("Formatting " + file.getName() + " ..."); } final ISourceViewer sourceViewer = getSourceViewer(); final IDocument document = sourceViewer.getDocument(); try { final Point initialSelection = new Point(-1, 0); Display.getDefault().syncExec(new Runnable() { @Override public void run() { Point selection = sourceViewer.getSelectedRange(); initialSelection.x = selection.x; initialSelection.y = selection.y; } }); final String unformattedSource = document.get(); final FormattedSource formatResult = DartFormatter.format( unformattedSource, initialSelection, monitor); Display.getDefault().syncExec(new Runnable() { @Override public void run() { if (!formatResult.source.equals(unformattedSource)) { try { document.replace( formatResult.changeOffset, formatResult.changeLength, formatResult.changeReplacement); sourceViewer.revealRange( formatResult.selectionOffset, formatResult.selectionLength); sourceViewer.setSelectedRange( formatResult.selectionOffset, formatResult.selectionLength); } catch (BadLocationException e) { DartToolsPlugin.log(e); } } } }); } catch (Exception e) { String msg = null; if (isParseFailure(e)) { msg = "fix parse errors and try again."; } else { msg = e.getMessage(); DartToolsPlugin.log(e); } if (PRINT_TO_CONSOLE) { console.clear(); console.println("Unable to format " + file.getName() + ": " + msg); } else { showMessage("Unable to format " + file.getName() + ": " + msg); } return Status.CANCEL_STATUS; } if (PRINT_TO_CONSOLE) { console.clear(); } return Status.OK_STATUS; } private boolean isParseFailure(Exception e) { String message = e.getMessage(); //TODO (pquitslund): use a return code or something better to signal parse failures return message != null && message.startsWith("Could not format because the source could not be parsed:"); } }; private boolean runNow; FormatElementAction(boolean runNow) { setEnabled(isEditorInputModifiable()); setText("Format"); this.runNow = runNow; } @Override public boolean isEnabled() { return super.isEnabled() && DartFormatter.isAvailable(); } @Override public void run() { IFile file = getInputResourceFile(); if (runNow) { new FormatElementJob(file).run(getProgressMonitor()); } else { new FormatElementJob(file).schedule(); } } @Override public void update() { setEnabled(isEditorInputModifiable()); } private void showMessage(final String msg) { final Display display = getSite().getShell().getDisplay(); display.syncExec(new Runnable() { @Override public void run() { IEditorStatusLine statusLine = (IEditorStatusLine) getAdapter(IEditorStatusLine.class); if (statusLine != null) { statusLine.setMessage(true, msg, null); } display.beep(); } }); } } /** * Text navigation action to navigate to the next sub-word. */ protected class NavigateNextSubWordAction extends NextSubWordAction { /** * Creates a new navigate next sub-word action. */ public NavigateNextSubWordAction() { super(ST.WORD_NEXT); } @Override protected void setCaretPosition(final int position) { getTextWidget().setCaretOffset(modelOffset2WidgetOffset(getSourceViewer(), position)); } } /** * Text navigation action to navigate to the previous sub-word. */ protected class NavigatePreviousSubWordAction extends PreviousSubWordAction { /** * Creates a new navigate previous sub-word action. */ public NavigatePreviousSubWordAction() { super(ST.WORD_PREVIOUS); } @Override protected void setCaretPosition(final int position) { getTextWidget().setCaretOffset(modelOffset2WidgetOffset(getSourceViewer(), position)); } } /** * Text navigation action to navigate to the next sub-word. */ protected abstract class NextSubWordAction extends TextNavigationAction { protected DartWordIterator fIterator = new DartWordIterator(); /** * Creates a new next sub-word action. * * @param code Action code for the default operation. Must be an action code from @see * org.eclipse.swt.custom.ST. */ protected NextSubWordAction(int code) { super(getSourceViewer().getTextWidget(), code); } @Override public void run() { // Check whether we are in a Dart code partition and the preference is enabled final IPreferenceStore store = getPreferenceStore(); if (!store.getBoolean(PreferenceConstants.EDITOR_SUB_WORD_NAVIGATION)) { super.run(); return; } final ISourceViewer viewer = getSourceViewer(); final IDocument document = viewer.getDocument(); fIterator.setText((CharacterIterator) new DocumentCharacterIterator(document)); int position = widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset()); if (position == -1) { return; } int next = findNextPosition(position); try { if (isBlockSelectionModeEnabled() && document.getLineOfOffset(next) != document.getLineOfOffset(position)) { super.run(); // may navigate into virtual white space } else if (next != BreakIterator.DONE) { setCaretPosition(next); getTextWidget().showSelection(); fireSelectionChanged(); } } catch (BadLocationException x) { // ignore } } /** * Finds the next position after the given position. * * @param position the current position * @return the next position */ protected int findNextPosition(int position) { ISourceViewer viewer = getSourceViewer(); int widget = -1; int next = position; while (next != BreakIterator.DONE && widget == -1) { // TODO: optimize next = fIterator.following(next); if (next != BreakIterator.DONE) { widget = modelOffset2WidgetOffset(viewer, next); } } IDocument document = viewer.getDocument(); LinkedModeModel model = LinkedModeModel.getModel(document, position); if (model != null) { LinkedPosition linkedPosition = model.findPosition(new LinkedPosition(document, position, 0)); if (linkedPosition != null) { int linkedPositionEnd = linkedPosition.getOffset() + linkedPosition.getLength(); if (position != linkedPositionEnd && linkedPositionEnd < next) { next = linkedPositionEnd; } } else { LinkedPosition nextLinkedPosition = model.findPosition(new LinkedPosition( document, next, 0)); if (nextLinkedPosition != null) { int nextLinkedPositionOffset = nextLinkedPosition.getOffset(); if (position != nextLinkedPositionOffset && nextLinkedPositionOffset < next) { next = nextLinkedPositionOffset; } } } } return next; } /** * Sets the caret position to the sub-word boundary given with <code>position</code>. * * @param position Position where the action should move the caret */ protected abstract void setCaretPosition(int position); } /** * Text navigation action to navigate to the previous sub-word. */ protected abstract class PreviousSubWordAction extends TextNavigationAction { protected DartWordIterator fIterator = new DartWordIterator(); /** * Creates a new previous sub-word action. * * @param code Action code for the default operation. Must be an action code from @see * org.eclipse.swt.custom.ST. */ protected PreviousSubWordAction(final int code) { super(getSourceViewer().getTextWidget(), code); } @Override public void run() { // Check whether we are in a Dart code partition and the preference is enabled final IPreferenceStore store = getPreferenceStore(); if (!store.getBoolean(PreferenceConstants.EDITOR_SUB_WORD_NAVIGATION)) { super.run(); return; } final ISourceViewer viewer = getSourceViewer(); final IDocument document = viewer.getDocument(); fIterator.setText((CharacterIterator) new DocumentCharacterIterator(document)); int position = widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset()); if (position == -1) { return; } int previous = findPreviousPosition(position); try { if (isBlockSelectionModeEnabled() && document.getLineOfOffset(previous) != document.getLineOfOffset(position)) { super.run(); // may navigate into virtual white space } else if (previous != BreakIterator.DONE) { setCaretPosition(previous); getTextWidget().showSelection(); fireSelectionChanged(); } } catch (BadLocationException x) { // ignore - getLineOfOffset failed } } /** * Finds the previous position before the given position. * * @param position the current position * @return the previous position */ protected int findPreviousPosition(int position) { ISourceViewer viewer = getSourceViewer(); int widget = -1; int previous = position; while (previous != BreakIterator.DONE && widget == -1) { // TODO: optimize previous = fIterator.preceding(previous); if (previous != BreakIterator.DONE) { widget = modelOffset2WidgetOffset(viewer, previous); } } IDocument document = viewer.getDocument(); LinkedModeModel model = LinkedModeModel.getModel(document, position); if (model != null) { LinkedPosition linkedPosition = model.findPosition(new LinkedPosition(document, position, 0)); if (linkedPosition != null) { int linkedPositionOffset = linkedPosition.getOffset(); if (position != linkedPositionOffset && previous < linkedPositionOffset) { previous = linkedPositionOffset; } } else { LinkedPosition previousLinkedPosition = model.findPosition(new LinkedPosition( document, previous, 0)); if (previousLinkedPosition != null) { int previousLinkedPositionEnd = previousLinkedPosition.getOffset() + previousLinkedPosition.getLength(); if (position != previousLinkedPositionEnd && previous < previousLinkedPositionEnd) { previous = previousLinkedPositionEnd; } } } } return previous; } /** * Sets the caret position to the sub-word boundary given with <code>position</code>. * * @param position Position where the action should move the caret */ protected abstract void setCaretPosition(int position); } /** * Text operation action to select the next sub-word. */ protected class SelectNextSubWordAction extends NextSubWordAction { /** * Creates a new select next sub-word action. */ public SelectNextSubWordAction() { super(ST.SELECT_WORD_NEXT); } @Override protected void setCaretPosition(final int position) { final ISourceViewer viewer = getSourceViewer(); final StyledText text = viewer.getTextWidget(); if (text != null && !text.isDisposed()) { final Point selection = text.getSelection(); final int caret = text.getCaretOffset(); final int offset = modelOffset2WidgetOffset(viewer, position); if (caret == selection.x) { text.setSelectionRange(selection.y, offset - selection.y); } else { text.setSelectionRange(selection.x, offset - selection.x); } } } } /** * Text operation action to select the previous sub-word. */ protected class SelectPreviousSubWordAction extends PreviousSubWordAction { /** * Creates a new select previous sub-word action. */ public SelectPreviousSubWordAction() { super(ST.SELECT_WORD_PREVIOUS); } @Override protected void setCaretPosition(final int position) { final ISourceViewer viewer = getSourceViewer(); final StyledText text = viewer.getTextWidget(); if (text != null && !text.isDisposed()) { final Point selection = text.getSelection(); final int caret = text.getCaretOffset(); final int offset = modelOffset2WidgetOffset(viewer, position); if (caret == selection.x) { text.setSelectionRange(selection.y, offset - selection.y); } else { text.setSelectionRange(selection.x, offset - selection.x); } } } } /** * This action implements smart home. Instead of going to the start of a line it does the * following: - if smart home/end is enabled and the caret is after the line's first * non-whitespace then the caret is moved directly before it, taking JavaDoc and multi-line * comments into account. - if the caret is before the line's first non-whitespace the caret is * moved to the beginning of the line - if the caret is at the beginning of the line see first * case. */ protected class SmartLineStartAction extends LineStartAction { /** * Creates a new smart line start action * * @param textWidget the styled text widget * @param doSelect a boolean flag which tells if the text up to the beginning of the line should * be selected */ public SmartLineStartAction(final StyledText textWidget, final boolean doSelect) { super(textWidget, doSelect); } @Override protected int getLineStartPosition(final IDocument document, final String line, final int length, final int offset) { String type = IDocument.DEFAULT_CONTENT_TYPE; try { type = TextUtilities.getContentType( document, DartPartitions.DART_PARTITIONING, offset, true); } catch (BadLocationException exception) { // Should not happen } int index = super.getLineStartPosition(document, line, length, offset); if (type.equals(DartPartitions.DART_DOC) || type.equals(DartPartitions.DART_MULTI_LINE_COMMENT)) { if (index < length - 1 && line.charAt(index) == '*' && line.charAt(index + 1) != '/') { do { ++index; } while (index < length && Character.isWhitespace(line.charAt(index))); } } else { if (index < length - 1 && line.charAt(index) == '/' && line.charAt(index + 1) == '/') { if (type.equals(DartPartitions.DART_SINGLE_LINE_DOC) && (index < length - 2 && line.charAt(index + 2) == '/')) { index++; } index++; do { ++index; } while (index < length && Character.isWhitespace(line.charAt(index))); } } return index; } } private class DartSelectionProvider extends SelectionProvider { private final Map<ISelectionChangedListener, ISelectionChangedListener> listeners = Maps.newHashMap(); @Override public void addPostSelectionChangedListener(ISelectionChangedListener listener) { ISelectionChangedListener dartListener = newDartSelectionListener(listener); super.addPostSelectionChangedListener(dartListener); } @Override public void addSelectionChangedListener(ISelectionChangedListener listener) { ISelectionChangedListener dartListener = newDartSelectionListener(listener); super.addSelectionChangedListener(dartListener); } @Override public ISelection getSelection() { ISelection selection = super.getSelection(); return newDartSelection(selection); } @Override public void removePostSelectionChangedListener(ISelectionChangedListener listener) { ISelectionChangedListener dartListener = listeners.remove(listener); if (dartListener != null) { super.removePostSelectionChangedListener(dartListener); } } @Override public void removeSelectionChangedListener(ISelectionChangedListener listener) { ISelectionChangedListener dartListener = listeners.remove(listener); if (dartListener != null) { super.removeSelectionChangedListener(dartListener); } } private ISelection newDartSelection(ISelection selection) { if (selection == null) { return new DartSelection(DartEditor.this, null, -1, 0); } if (selection instanceof MarkSelection) { MarkSelection sel = (MarkSelection) selection; IDocument doc = sel.getDocument(); return new DartSelection(DartEditor.this, doc, sel.getOffset(), sel.getLength()); } ITextSelection textSelection = (ITextSelection) selection; // prepare document ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer == null) { return new DartSelection(DartEditor.this, null, -1, 0); } IDocument document = sourceViewer.getDocument(); // prepare AssistContext AssistContext assistContext = getAssistContext(textSelection); // OK, wrap into DartSelection return new DartSelection( DartEditor.this, document, textSelection.getOffset(), textSelection.getLength()); } private ISelectionChangedListener newDartSelectionListener( final ISelectionChangedListener listener) { ISelectionChangedListener dartListener = new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { listener.selectionChanged(new SelectionChangedEvent( event.getSelectionProvider(), newDartSelection(event.getSelection()))); } }; listeners.put(listener, dartListener); return dartListener; } } /** * Updates the outline page selection and this editor's range indicator. */ private class EditorSelectionChangedListener extends AbstractSelectionChangedListener { @Override public void selectionChanged(SelectionChangedEvent event) { // XXX: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=56161 DartEditor.this.selectionChanged(); } } /** * An adapter from new to old source reference abstractions. */ private static final class SourceReferenceAdapter implements SourceReference { private final SourceRange sourceRange; private SourceReferenceAdapter(com.google.dart.engine.element.Element element) { sourceRange = new SourceRange(element.getNameOffset(), element.getDisplayName().length()); } @Override public boolean exists() { // Unused in this context return true; } @Override public SourceRange getNameRange() throws DartModelException { return sourceRange; } @Override public String getSource() throws DartModelException { // Unused in this context return ""; } @Override public SourceRange getSourceRange() throws DartModelException { // Unused in this context return sourceRange; } } /** * 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> */ private final class ToggleFoldingRunner implements IPartListener2 { /** * The workbench page we registered the part listener with, or <code>null</code>. */ private IWorkbenchPage fPage; @Override public void partActivated(IWorkbenchPartReference partRef) { } @Override public void partBroughtToTop(IWorkbenchPartReference partRef) { } @Override public void partClosed(IWorkbenchPartReference partRef) { if (DartEditor.this.equals(partRef.getPart(false))) { cancel(); } } @Override public void partDeactivated(IWorkbenchPartReference partRef) { } @Override public void partHidden(IWorkbenchPartReference partRef) { } @Override public void partInputChanged(IWorkbenchPartReference partRef) { } @Override public void partOpened(IWorkbenchPartReference partRef) { } @Override public void partVisible(IWorkbenchPartReference partRef) { if (DartEditor.this.equals(partRef.getPart(false))) { cancel(); toggleFolding(); } } /** * 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; } IWorkbenchPartSite site = getSite(); if (site != null) { IWorkbenchPage page = site.getPage(); if (!page.isPartVisible(DartEditor.this)) { // if we're not visible - defer until visible fPage = page; fFoldingRunner = this; page.addPartListener(this); return; } } // we're visible - run now toggleFolding(); } /** * Remove the listener and clear the field. */ private void cancel() { if (fPage != null) { fPage.removePartListener(this); fPage = null; } if (fFoldingRunner == this) { fFoldingRunner = null; } } /** * Does the actual toggling of projection. */ private void toggleFolding() { ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer instanceof ProjectionViewer) { ProjectionViewer pv = (ProjectionViewer) sourceViewer; if (pv.isProjectionMode() != isFoldingEnabled()) { if (pv.canDoOperation(ProjectionViewer.TOGGLE)) { pv.doOperation(ProjectionViewer.TOGGLE); } } } } } private static boolean isBracket(char character) { for (int i = 0; i != BRACKETS.length; ++i) { if (character == BRACKETS[i]) { return true; } } return false; } private static 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; } } private IFile inputResourceFile; private File inputJavaFile; private volatile com.google.dart.engine.ast.CompilationUnit resolvedUnit; private SourceRange textSelectionRange; /** Preference key for matching brackets */ protected final static String MATCHING_BRACKETS = PreferenceConstants.EDITOR_MATCHING_BRACKETS; /** Preference key for matching brackets color */ protected final static String MATCHING_BRACKETS_COLOR = PreferenceConstants.EDITOR_MATCHING_BRACKETS_COLOR; protected final static char[] BRACKETS = {'{', '}', '(', ')', '[', ']', '<', '>'}; /** The outline page */ protected DartOutlinePage fOutlinePage_OLD; /** The outline page */ protected DartOutlinePage_NEW fOutlinePage_NEW; private Outline outline; private AnalysisServerNavigationListener navigationListener = new AnalysisServerNavigationListener() { @Override public void computedNavigation(String file, NavigationRegion[] regions) { } }; private Outline lastOutline; private AnalysisServerOutlineListener outlineListener = new AnalysisServerOutlineListener() { @Override public void computedOutline(String file, final Outline outline) { DartEditor.this.outline = outline; Display.getDefault().asyncExec(new Runnable() { @Override public void run() { lastOutline = outline; updateOutlinePageNew(outline); } }); } }; private SemanticHighlightingManager_NEW analysisServerHighlightManager; /** Outliner context menu Id */ protected String fOutlinerContextMenuId; /** * The editor selection changed listener. */ private EditorSelectionChangedListener fEditorSelectionChangedListener; /** The editor's bracket matcher */ protected DartPairMatcher fBracketMatcher = new DartPairMatcher(BRACKETS); /** This editor's encoding support */ private DefaultEncodingSupport fEncodingSupport; /** History for structure select action */ private SelectionHistory fSelectionHistory; protected CompositeActionGroup fActionGroups; /** * The action group for folding. */ private FoldingActionGroup fFoldingGroup; private CompositeActionGroup fOpenEditorActionGroup; /** * Removes trailing whitespace on editor saves. */ private RemoveTrailingWhitespaceAction removeTrailingWhitespaceAction; private MarkOccurrencesManager_OLD occurrencesManager_OLD; private MarkOccurrencesManager_NEW occurrencesManager_NEW; /** * This editor's projection support */ private ProjectionSupport fProjectionSupport; /** * This editor's projection model updater */ private IDartFoldingStructureProvider fProjectionModelUpdater; /** * The override and implements indicator manager for this editor. */ protected OverrideIndicatorManager fOverrideIndicatorManager; /** * Semantic highlighting manager */ protected SemanticHighlightingManager fSemanticManager; /** * The folding runner. */ private ToggleFoldingRunner fFoldingRunner; /** * Tells whether the selection changed event is caused by a call to * {@link #gotoAnnotation(boolean)}. */ private boolean fSelectionChangedViaGotoAnnotation; /** * The cached selected range. * * @see ITextViewer#getSelectedRange() */ private Point fCachedSelectedRange; private ShowSelectionLabelAction showSelectionLabel = new ShowSelectionLabelAction(); private SelectionProvider selectionProvider = new DartSelectionProvider(); private final List<ISelectionChangedListener> dartSelectionListeners = Lists.newArrayList(); private final CaretListener dartSelectionCaretListener = new CaretListener() { private boolean caretMovedScheduled = false; @Override public void caretMoved(CaretEvent event) { // already scheduled and not executed yet if (caretMovedScheduled) { return; } caretMovedScheduled = true; // schedule selection update Display.getCurrent().asyncExec(new Runnable() { @Override public void run() { caretMovedScheduled = false; if (isDisposed()) { return; } applySelectionToOutline(); fireDartSelectionListeners(); } }); } }; //Patched to address dartbug.com/7998 private ISelectionChangedListener patchedSelectionChangedListener; /** * Default constructor. */ public DartEditor() { super(); } /** * Specifies that given {@link ISelectionChangedListener} should be invoked on any * {@link DartSelection} change - selection, {@link CompilationUnit}. */ public void addDartSelectionListener(ISelectionChangedListener listener) { dartSelectionListeners.add(listener); } @Override public void applyResolvedUnit(com.google.dart.engine.ast.CompilationUnit unit) { if (isDisposed()) { return; } // ignore if already know that we don't have resolved unit if (resolvedUnit == null && unit == null) { return; } // ignore if this unit has already been set if (resolvedUnit == unit) { return; } // OK, schedule selection update resolvedUnit = unit; ExecutionUtils.runLogAsync(new RunnableEx() { @Override public void run() { if (isDisposed()) { return; } // update Outline if (resolvedUnit != null && fOutlinePage_OLD != null) { if (fOutlinePage_OLD != null) { fOutlinePage_OLD.setInput(resolvedUnit); } applySelectionToOutline(); } // update selection listeners fireDartSelectionListeners(); } }); } /** * Collapses all foldable comments if supported by the folding structure provider. */ public void collapseComments() { DartX.todo("folding"); if (fProjectionModelUpdater instanceof IDartFoldingStructureProviderExtension) { IDartFoldingStructureProviderExtension extension = (IDartFoldingStructureProviderExtension) fProjectionModelUpdater; extension.collapseComments(); } } /** * Collapses all foldable members if supported by the folding structure provider. */ public void collapseMembers() { DartX.todo("folding"); if (fProjectionModelUpdater instanceof IDartFoldingStructureProviderExtension) { IDartFoldingStructureProviderExtension extension = (IDartFoldingStructureProviderExtension) fProjectionModelUpdater; extension.collapseMembers(); } } public ISelection createElementSelection() { ITextSelection textSelection = (ITextSelection) getSelectionProvider().getSelection(); IDocument document = getSourceViewer().getDocument(); return textSelection; } @Override public void createPartControl(Composite parent) { super.createPartControl(parent); patchSelectionChangeParticipation(); getSourceViewer().getTextWidget().addCaretListener(dartSelectionCaretListener); // Set tab width IPreferenceStore store = getPreferenceStore(); if (store != null) { int tabWidth = store.getInt(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_TAB_WIDTH); getSourceViewer().getTextWidget().setTabs(tabWidth); } fEditorSelectionChangedListener = new EditorSelectionChangedListener(); fEditorSelectionChangedListener.install(getSelectionProvider()); installSemanticHighlighting(); } @Override public void dispose() { if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) { AnalysisServerData analysisServerData = DartCore.getAnalysisServerData(); String file = getInputFilePath(); if (file != null) { analysisServerData.removeNavigationListener(file, navigationListener); analysisServerData.removeOutlineListener(file, outlineListener); } } DartX.todo("folding"); if (fProjectionModelUpdater != null) { fProjectionModelUpdater.uninstall(); fProjectionModelUpdater = null; } if (fProjectionSupport != null) { fProjectionSupport.dispose(); fProjectionSupport = null; } uninstallOccurrencesFinder(); uninstallOverrideIndicator(); uninstallSemanticHighlighting(); if (fEncodingSupport != null) { fEncodingSupport.dispose(); fEncodingSupport = null; } if (fBracketMatcher != null) { fBracketMatcher.dispose(); fBracketMatcher = null; } if (fSelectionHistory != null) { fSelectionHistory.dispose(); fSelectionHistory = null; } if (fEditorSelectionChangedListener != null) { fEditorSelectionChangedListener.uninstall(getSelectionProvider()); fEditorSelectionChangedListener = null; } if (fActionGroups != null) { fActionGroups.dispose(); fActionGroups = null; } selectionProvider = null; super.dispose(); resolvedUnit = null; } /** * Run the editor format action. */ public void doFormat() { getAction(DartEditorActionDefinitionIds.QUICK_FORMAT).run(); } @Override public void editorContextMenuAboutToShow(IMenuManager menu) { menu.add(new Separator(ITextEditorActionConstants.GROUP_OPEN)); menu.add(new Separator(ITextEditorActionConstants.GROUP_EDIT)); menu.add(new Separator(ITextEditorActionConstants.GROUP_RESTORE)); menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); ISelection selection = createElementSelection(); // Open Declaration action ActionContext context = new ActionContext(selection); setContextMenuContext(menu, context); // This context contains a DartElementSelection for menus. // Quick Type Hierarchy if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) { IAction action = getAction(DartEditorActionDefinitionIds.OPEN_HIERARCHY); if (action != null && action.isEnabled()) { menu.appendToGroup(IContextMenuConstants.GROUP_OPEN, action); } } else { if (selection instanceof DartSelection) { DartSelection dartSelection = (DartSelection) selection; if (ActionUtil.isOpenHierarchyAvailable(dartSelection)) { IAction action = getAction(DartEditorActionDefinitionIds.OPEN_HIERARCHY); menu.appendToGroup(IContextMenuConstants.GROUP_OPEN, action); } } } // Quick Outline if ((selection instanceof TextSelection) && ((TextSelection) selection).getLength() == 0) { IAction action = getAction(DartEditorActionDefinitionIds.SHOW_OUTLINE); menu.appendToGroup(ITextEditorActionConstants.GROUP_RESTORE, action); } // Revert action addAction( menu, ITextEditorActionConstants.GROUP_RESTORE, ITextEditorActionConstants.REVERT_TO_SAVED); // Cut/Copy/Paste actions addAction(menu, ITextEditorActionConstants.GROUP_EDIT, ITextEditorActionConstants.UNDO); addAction(menu, ITextEditorActionConstants.GROUP_EDIT, ITextEditorActionConstants.CUT); addAction(menu, ITextEditorActionConstants.GROUP_EDIT, ITextEditorActionConstants.COPY); addAction(menu, ITextEditorActionConstants.GROUP_EDIT, ITextEditorActionConstants.PASTE); // Quick Assist { IAction action = getAction(ITextEditorActionConstants.QUICK_ASSIST); if (action != null && action.isEnabled()) { addAction( menu, RefactorActionGroup_NEW.GROUP_REORG, ITextEditorActionConstants.QUICK_ASSIST); } } // Format IAction action = getAction(DartEditorActionDefinitionIds.QUICK_FORMAT); if (action != null && action.isEnabled()) { addAction( menu, RefactorActionGroup_NEW.GROUP_REORG, DartEditorActionDefinitionIds.QUICK_FORMAT); } if (selection instanceof DartSelection) { DartSelection dartSelection = (DartSelection) selection; if (ActionUtil.hasItemsInGroup(menu, IContextMenuConstants.GROUP_OPEN)) { showSelectionLabel.update(dartSelection); showSelectionLabel.setEnabled(false); if (showSelectionLabel.getText() != null) { menu.prependToGroup(IContextMenuConstants.GROUP_OPEN, showSelectionLabel); } } } } @SuppressWarnings("rawtypes") @Override public Object getAdapter(Class required) { DartX.todo("outline"); if (IContentOutlinePage.class.equals(required)) { if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) { if (fOutlinePage_NEW == null) { fOutlinePage_NEW = createOutlinePage_NEW(); updateOutlinePageNew(lastOutline); } return fOutlinePage_NEW; } else { if (fOutlinePage_OLD == null) { fOutlinePage_OLD = createOutlinePage_OLD(); } return fOutlinePage_OLD; } } if (IEncodingSupport.class.equals(required)) { return fEncodingSupport; } if (required == IShowInTargetList.class) { return new IShowInTargetList() { @Override public String[] getShowInTargetIds() { String explorerViewID = ProductProperties.getProperty(IProductConstants.PERSPECTIVE_EXPLORER_VIEW); // make sure the specified view ID is known if (PlatformUI.getWorkbench().getViewRegistry().find(explorerViewID) == null) { explorerViewID = IPageLayout.ID_PROJECT_EXPLORER; } return new String[] {explorerViewID, IPageLayout.ID_OUTLINE, IPageLayout.ID_RES_NAV}; } }; } DartX.todo("hover"); if (required == IShowInSource.class) { return new IShowInSource() { @Override public ShowInContext getShowInContext() { return new ShowInContext(getEditorInput(), null) { @Override public ISelection getSelection() { return null; } }; } }; } DartX.todo("folding"); if (required == IDartFoldingStructureProvider.class) { return fProjectionModelUpdater; } if (fProjectionSupport != null) { Object adapter = fProjectionSupport.getAdapter(getSourceViewer(), required); if (adapter != null) { return adapter; } } if (required == IContextProvider.class) { return DartUIHelp.getHelpContextProvider(this, DartHelpContextIds.JAVA_EDITOR); } return super.getAdapter(required); } /** * @return the {@link AssistContext} with resolved {@link CompilationUnit}, selection and * {@link SearchEngine}. May be <code>null</code> if underlying {@link CompilationUnit} is * not resolved. */ public AssistContext getAssistContext() { ITextSelection textSelection = (ITextSelection) getSelectionProvider().getSelection(); return getAssistContext(textSelection); } /** * Returns the cached selected range, which allows to query it from a non-UI thread. * <p> * The result might be outdated if queried from a non-UI thread.</em> * </p> * * @return the caret offset in the master document * @see ITextViewer#getSelectedRange() */ public Point getCachedSelectedRange() { return fCachedSelectedRange; } /** * Answer the {@link DartReconcilingStrategy} associated with this editor. * * @return the strategy or {@code null} if none */ public DartReconcilingStrategy getDartReconcilingStrategy() { return null; } public Point getDocumentSelectionRange() { ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer == null) { return null; } StyledText styledText = sourceViewer.getTextWidget(); if (styledText == null) { return null; } int widgetOffset = styledText.getCaretOffset(); int widgetLength = styledText.getSelection().y; if (sourceViewer instanceof ITextViewerExtension5) { ITextViewerExtension5 extension = (ITextViewerExtension5) sourceViewer; int docOffset = extension.widgetOffset2ModelOffset(widgetOffset); return new Point(docOffset, widgetLength); } else { int offset = sourceViewer.getVisibleRegion().getOffset(); int docOffset = offset + widgetOffset; return new Point(docOffset, widgetLength); } } @Override public AnalysisContext getInputAnalysisContext() { if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) { throw new IllegalStateException("This method cannot be used with Analysis Server enabled."); } if (inputResourceFile != null) { return DartCore.getProjectManager().getContext(inputResourceFile); } if (inputJavaFile != null) { return DartCore.getProjectManager().getSdkContext(); } return null; } /** * Alternative to {@link #getInputDartElement()} that returns the Analysis engine * {@link com.google.dart.engine.element.Element} instead of a {@link DartElement}. */ public com.google.dart.engine.element.CompilationUnitElement getInputElement() { if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) { throw new IllegalStateException("This method cannot be used with Analysis Server enabled."); } com.google.dart.engine.ast.CompilationUnit unit = getInputUnit(); return unit == null ? null : unit.getElement(); } @Override public String getInputFilePath() { // may be workspace IFile if (inputResourceFile != null) { IPath location = inputResourceFile.getLocation(); if (location == null) { return null; } return location.toOSString(); } // may be Java file if (inputJavaFile != null) { return inputJavaFile.getAbsolutePath(); } // TODO(scheglov) Analysis Server return null; } @Override public Project getInputProject() { if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) { throw new IllegalStateException("This method cannot be used with Analysis Server enabled."); } if (inputResourceFile != null) { IProject resourceProject = inputResourceFile.getProject(); return DartCore.getProjectManager().getProject(resourceProject); } return null; } /** * @return the input {@link IFile}, may be <code>null</code> if different kind of input. */ public IFile getInputResourceFile() { return inputResourceFile; } @Override public Source getInputSource() { if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) { throw new IllegalStateException("This method should not be used with Server."); } ProjectManager projectManager = DartCore.getProjectManager(); // may be workspace IFile if (inputResourceFile != null) { return projectManager.getSource(inputResourceFile); } // may be SDK if (inputJavaFile != null) { DartSdk sdk = projectManager.getSdk(); if (sdk instanceof DirectoryBasedDartSdk) { if (inputJavaFile.getAbsolutePath().startsWith( ((DirectoryBasedDartSdk) sdk).getDirectory().getAbsolutePath() + File.separator)) { return sdk.fromFileUri(inputJavaFile.toURI()); } } return new FileBasedSource(inputJavaFile); } // some random external file return null; } public com.google.dart.engine.ast.CompilationUnit getInputUnit() { if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) { throw new IllegalStateException("This method cannot be used with Analysis Server enabled."); } return resolvedUnit; } @Override public int getOrientation() { return SWT.LEFT_TO_RIGHT; // Dart editors are always left to right by default } /** * Returns the latest known outline. */ public Outline getOutline() { return outline; } public IPreferenceStore getPreferences() { return super.getPreferenceStore(); } @Override public ISelectionProvider getSelectionProvider() { return selectionProvider; } /** * @return the last reported text selection range in underlying {@link #getSourceViewer()}. May be * <code>null</code> if selection in not known yet. This method will not make any UI * method invocation. */ public SourceRange getTextSelectionRange() { return textSelectionRange; } @Override public String getTitleToolTip() { if (getEditorInput() instanceof IFileEditorInput) { IFileEditorInput input = (IFileEditorInput) getEditorInput(); if (input.getFile().getLocation() != null) { return input.getFile().getLocation().toFile().toString(); } } return super.getTitleToolTip(); } public final ISourceViewer getViewer() { return getSourceViewer(); } @Override public Annotation gotoAnnotation(boolean forward) { fSelectionChangedViaGotoAnnotation = true; return super.gotoAnnotation(forward); } /** * Jumps to the matching bracket. */ public void gotoMatchingBracket() { 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(DartEditorMessages.GotoMatchingBracket_error_invalidSelection); 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(DartEditorMessages.GotoMatchingBracket_error_noMatchingBracket); 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(DartEditorMessages.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()); } @Override public boolean isDirty() { return isContentEditable() ? super.isDirty() : false; } @Override public boolean isEditable() { return isContentEditable() ? super.isEditable() : false; } @Override public boolean isEditorInputModifiable() { return isContentEditable() ? super.isEditorInputModifiable() : false; } @Override public boolean isEditorInputReadOnly() { return isContentEditable() ? super.isEditorInputReadOnly() : true; } /** * @return {@code true} if the editor's content is visible */ @Override public boolean isVisible() { ISourceViewer viewer = getViewer(); if (viewer != null) { StyledText widget = viewer.getTextWidget(); if (widget != null) { return widget.isVisible(); } } return false; } /** * Informs the editor that its outliner has been closed. */ public void outlinePageClosed() { if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) { if (fOutlinePage_NEW != null) { fOutlinePage_NEW = null; resetHighlightRange(); } } else { if (fOutlinePage_OLD != null) { fOutlinePage_OLD = null; resetHighlightRange(); } } } public void removeDartSelectionListener(ISelectionChangedListener listener) { dartSelectionListeners.remove(listener); } /** * Resets the foldings structure according to the folding preferences. */ public void resetProjection() { DartX.todo("folding"); if (fProjectionModelUpdater != null) { fProjectionModelUpdater.initialize(); } } public void selectEndReveal(com.google.dart.engine.element.Element element) { if (element == null || element instanceof CompilationUnitElement) { return; } markInNavigationHistory(); int offset = element.getNameOffset(); int length = element.getDisplayName().length(); selectAndReveal(offset, length); } public void setPreferences(IPreferenceStore store) { uninstallSemanticHighlighting(); super.setPreferenceStore(store); installSemanticHighlighting(); } public void setSelection_NEW(Outline outline, boolean moveCursor) { // validate Outline if (outline == null) { return; } // prepare range int offset = outline.getElement().getLocation().getOffset(); int length = outline.getElement().getLocation().getLength(); // prepare ISourceViewer ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer == null) { return; } // highlight range (not selection - just highlighting on left editor band) if (offset < 0) { return; } setHighlightRange(offset, length, moveCursor); // do we want to change selection? if (!moveCursor) { return; } // prepare StyledText StyledText textWidget = sourceViewer.getTextWidget(); if (textWidget == null) { return; } // set selection in StyledText try { textWidget.setRedraw(false); sourceViewer.revealRange(offset, length); sourceViewer.setSelectedRange(offset, length); } finally { textWidget.setRedraw(true); } } public void setSelection_OLD(LightNodeElement element, boolean moveCursor) { // validate LightNodeElement if (element == null) { return; } // prepare range int offset = element.getNameOffset(); int length = element.getNameLength(); // prepare ISourceViewer ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer == null) { return; } // highlight range (not selection - just highlighting on left editor band) if (offset < 0) { return; } setHighlightRange(offset, length, moveCursor); // do we want to change selection? if (!moveCursor) { return; } // prepare StyledText StyledText textWidget = sourceViewer.getTextWidget(); if (textWidget == null) { return; } // set selection in StyledText try { textWidget.setRedraw(false); sourceViewer.revealRange(offset, length); sourceViewer.setSelectedRange(offset, length); } finally { textWidget.setRedraw(true); } } /** * Synchronizes the outliner selection with the actual cursor position in the editor. */ public void synchronizeOutlinePageSelection() { // TODO(scheglov) // synchronizeOutlinePage(computeHighlightRangeSourceElement()); } public void updatedTitleImage(Image image) { setTitleImage(image); } @Override protected void adjustHighlightRange(int offset, int length) { if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) { if (fOutlinePage_NEW != null) { fOutlinePage_NEW.select(offset); } } else { Object element = getElementAt(offset, false); if (element instanceof LightNodeElement) { LightNodeElement sourceElement = (LightNodeElement) element; int elementOffset = sourceElement.getNameOffset(); int elementLength = sourceElement.getNameLength(); ISourceViewer viewer = getSourceViewer(); if (viewer instanceof ITextViewerExtension5) { ITextViewerExtension5 extension = (ITextViewerExtension5) viewer; extension.exposeModelRange(new Region(elementOffset, elementLength)); } setHighlightRange(elementOffset, elementLength, true); if (fOutlinePage_OLD != null) { fOutlinePage_OLD.select((LightNodeElement) element); } } } ISourceViewer viewer = getSourceViewer(); if (viewer instanceof ITextViewerExtension5) { ITextViewerExtension5 extension = (ITextViewerExtension5) viewer; extension.exposeModelRange(new Region(offset, length)); } else { resetHighlightRange(); } } /** * Determines whether the preference change encoded by the given event changes the override * indication. * * @param event the event to be investigated * @return <code>true</code> if event causes a change */ protected boolean affectsOverrideIndicatorAnnotations(PropertyChangeEvent event) { String key = event.getProperty(); AnnotationPreference preference = getAnnotationPreferenceLookup().getAnnotationPreference( OverrideIndicatorManager.ANNOTATION_TYPE); if (key == null || preference == null) { return false; } return key.equals(preference.getHighlightPreferenceKey()) || key.equals(preference.getVerticalRulerPreferenceKey()) || key.equals(preference.getOverviewRulerPreferenceKey()) || key.equals(preference.getTextPreferenceKey()); } @Override protected boolean affectsTextPresentation(PropertyChangeEvent event) { return ((DartSourceViewerConfiguration) getSourceViewerConfiguration()).affectsTextPresentation(event) || super.affectsTextPresentation(event); } protected void checkEditableState() { // isEditableStateKnown = false; } @Override protected String[] collectContextMenuPreferencePages() { String[] inheritedPages = super.collectContextMenuPreferencePages(); int length = 10; String[] result = new String[inheritedPages.length + length]; result[0] = "com.google.dart.tools.ui.internal.preferences.JavaEditorPreferencePage"; //$NON-NLS-1$ result[1] = "com.google.dart.tools.ui.internal.preferences.JavaTemplatePreferencePage"; //$NON-NLS-1$ result[2] = "com.google.dart.tools.ui.internal.preferences.CodeAssistPreferencePage"; //$NON-NLS-1$ result[3] = "com.google.dart.tools.ui.internal.preferences.CodeAssistPreferenceAdvanced"; //$NON-NLS-1$ result[4] = "com.google.dart.tools.ui.internal.preferences.JavaEditorHoverPreferencePage"; //$NON-NLS-1$ result[5] = "com.google.dart.tools.ui.internal.preferences.JavaEditorColoringPreferencePage"; //$NON-NLS-1$ result[6] = "com.google.dart.tools.ui.internal.preferences.FoldingPreferencePage"; //$NON-NLS-1$ result[7] = "com.google.dart.tools.ui.internal.preferences.MarkOccurrencesPreferencePage"; //$NON-NLS-1$ result[8] = "com.google.dart.tools.ui.internal.preferences.SmartTypingPreferencePage"; //$NON-NLS-1$ result[9] = "com.google.dart.tools.ui.internal.preferences.SaveParticipantPreferencePage"; //$NON-NLS-1$ System.arraycopy(inheritedPages, 0, result, length, inheritedPages.length); return result; } /** * Computes and returns the source element that includes the caret and serves as provider for the * outline page selection and the editor range indication. * <p> * to replace {@link #computeHighlightRangeSourceReference()}. * * @return the computed source element */ protected LightNodeElement computeHighlightRangeSourceElement( com.google.dart.engine.ast.CompilationUnit unit, int caret) { if (unit == null) { return null; } // ISourceViewer sourceViewer = getSourceViewer(); // if (sourceViewer == null) { // return null; // } // // StyledText styledText = sourceViewer.getTextWidget(); // if (styledText == null) { // return null; // } // // int caret = 0; // if (sourceViewer instanceof ITextViewerExtension5) { // ITextViewerExtension5 extension = (ITextViewerExtension5) sourceViewer; // caret = extension.widgetOffset2ModelOffset(styledText.getCaretOffset()); // } else { // int offset = sourceViewer.getVisibleRegion().getOffset(); // caret = offset + styledText.getCaretOffset(); // } AstNode node = new NodeLocator(caret).searchWithin(unit); // May be whitespace between class declaration {}, try to find class member. if (node instanceof ClassDeclaration) { ClassDeclaration classDeclaration = (ClassDeclaration) node; if (classDeclaration.getLeftBracket().getOffset() + 1 < caret && caret < classDeclaration.getRightBracket().getOffset()) { for (ClassMember member : classDeclaration.getMembers()) { if (caret < member.getOffset()) { node = member; break; } } } } // May be unit whitespace, try to find unit member. if (node instanceof com.google.dart.engine.ast.CompilationUnit) { NodeList<CompilationUnitMember> declarations = ((com.google.dart.engine.ast.CompilationUnit) node).getDeclarations(); for (CompilationUnitMember member : declarations) { if (caret < member.getOffset()) { node = member; break; } } } return LightNodeElements.createLightNodeElement(inputResourceFile, node); } /** * Computes and returns the source reference that includes the caret and serves as provider for * the outline page selection and the editor range indication. * * @return the computed source reference */ protected SourceReference computeHighlightRangeSourceReference() { ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer == null) { return null; } StyledText styledText = sourceViewer.getTextWidget(); if (styledText == null) { return null; } int caret = 0; if (sourceViewer instanceof ITextViewerExtension5) { ITextViewerExtension5 extension = (ITextViewerExtension5) sourceViewer; caret = extension.widgetOffset2ModelOffset(styledText.getCaretOffset()); } else { int offset = sourceViewer.getVisibleRegion().getOffset(); caret = offset + styledText.getCaretOffset(); } Object element = getElementAt(caret, false); if (!(element instanceof SourceReference)) { return null; } return (SourceReference) element; } @Override protected void configureSourceViewerDecorationSupport(SourceViewerDecorationSupport support) { support.setCharacterPairMatcher(fBracketMatcher); support.setMatchingCharacterPainterPreferenceKeys(MATCHING_BRACKETS, MATCHING_BRACKETS_COLOR); super.configureSourceViewerDecorationSupport(support); support.setMarginPainterPreferenceKeys( DartFormatter.PRINT_MARGIN, DartFormatter.PRINT_MARGIN_COLOR, DartFormatter.PRINT_MARGIN_COLUMN); } @Override protected void createActions() { installEncodingSupport(); super.createActions(); DartX.todo("actions"); ActionGroup oeg, ovg; ActionGroup dsg, ddg; if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) { fActionGroups = new CompositeActionGroup(new ActionGroup[] { oeg = new OpenEditorActionGroup_NEW(this), ovg = new OpenViewActionGroup_NEW(this), dsg = new DartSearchActionGroup_NEW(this)}); fOpenEditorActionGroup = new CompositeActionGroup(new ActionGroup[] {ovg, oeg, dsg}); } else { fActionGroups = new CompositeActionGroup(new ActionGroup[] { oeg = new OpenEditorActionGroup_OLD(this), ovg = new OpenViewActionGroup_OLD(this), dsg = new DartSearchActionGroup_OLD(this), ddg = new DartdocActionGroup_OLD(this)}); fOpenEditorActionGroup = new CompositeActionGroup(new ActionGroup[] {ovg, oeg, dsg, ddg}); } // Registers the folding actions with the editor fFoldingGroup = new FoldingActionGroup(this, getViewer()); Action action = new GotoMatchingBracketAction(this); action.setActionDefinitionId(DartEditorActionDefinitionIds.GOTO_MATCHING_BRACKET); setAction(GotoMatchingBracketAction.GOTO_MATCHING_BRACKET, action); action = new TextOperationAction( DartEditorMessages.getBundleForConstructedKeys(), "ShowOutline.", this, DartSourceViewer.SHOW_OUTLINE, true); //$NON-NLS-1$ action.setActionDefinitionId(DartEditorActionDefinitionIds.SHOW_OUTLINE); setAction(DartEditorActionDefinitionIds.SHOW_OUTLINE, action); PlatformUI.getWorkbench().getHelpSystem().setHelp( action, DartHelpContextIds.SHOW_OUTLINE_ACTION); action = new TextOperationAction( DartEditorMessages.getBundleForConstructedKeys(), "OpenStructure.", this, DartSourceViewer.OPEN_STRUCTURE, true); //$NON-NLS-1$ action.setActionDefinitionId(DartEditorActionDefinitionIds.OPEN_STRUCTURE); setAction(DartEditorActionDefinitionIds.OPEN_STRUCTURE, action); PlatformUI.getWorkbench().getHelpSystem().setHelp( action, DartHelpContextIds.OPEN_STRUCTURE_ACTION); action = new TextOperationAction( DartEditorMessages.getBundleForConstructedKeys(), "OpenHierarchy.", this, DartSourceViewer.SHOW_HIERARCHY, true); //$NON-NLS-1$ action.setActionDefinitionId(DartEditorActionDefinitionIds.OPEN_HIERARCHY); setAction(DartEditorActionDefinitionIds.OPEN_HIERARCHY, action); PlatformUI.getWorkbench().getHelpSystem().setHelp( action, DartHelpContextIds.OPEN_HIERARCHY_ACTION); fSelectionHistory = new SelectionHistory(this); if (!DartCoreDebug.ENABLE_ANALYSIS_SERVER) { action = new StructureSelectEnclosingAction(this, fSelectionHistory); action.setActionDefinitionId(DartEditorActionDefinitionIds.SELECT_ENCLOSING); setAction(StructureSelectionAction.ENCLOSING, action); action = new StructureSelectNextAction(this, fSelectionHistory); action.setActionDefinitionId(DartEditorActionDefinitionIds.SELECT_NEXT); setAction(StructureSelectionAction.NEXT, action); action = new StructureSelectPreviousAction(this, fSelectionHistory); action.setActionDefinitionId(DartEditorActionDefinitionIds.SELECT_PREVIOUS); setAction(StructureSelectionAction.PREVIOUS, action); StructureSelectHistoryAction historyAction = new StructureSelectHistoryAction( this, fSelectionHistory); historyAction.setActionDefinitionId(DartEditorActionDefinitionIds.SELECT_LAST); setAction(StructureSelectionAction.HISTORY, historyAction); fSelectionHistory.setHistoryAction(historyAction); } action = GoToNextPreviousMemberAction.newGoToNextMemberAction(this); action.setActionDefinitionId(DartEditorActionDefinitionIds.GOTO_NEXT_MEMBER); setAction(GoToNextPreviousMemberAction.NEXT_MEMBER, action); action = GoToNextPreviousMemberAction.newGoToPreviousMemberAction(this); action.setActionDefinitionId(DartEditorActionDefinitionIds.GOTO_PREVIOUS_MEMBER); setAction(GoToNextPreviousMemberAction.PREVIOUS_MEMBER, action); action = new FormatElementAction(false); action.setActionDefinitionId(DartEditorActionDefinitionIds.QUICK_FORMAT); setAction(DartEditorActionDefinitionIds.QUICK_FORMAT, action); markAsStateDependentAction(DartEditorActionDefinitionIds.QUICK_FORMAT, true); action = new RemoveOccurrenceAnnotations(this); action.setActionDefinitionId(DartEditorActionDefinitionIds.REMOVE_OCCURRENCE_ANNOTATIONS); setAction("RemoveOccurrenceAnnotations", action); //$NON-NLS-1$ // add annotation actions for roll-over expand hover action = new DartSelectMarkerRulerAction2( DartEditorMessages.getBundleForConstructedKeys(), "Editor.RulerAnnotationSelection.", this); //$NON-NLS-1$ setAction("AnnotationAction", action); //$NON-NLS-1$ DartX.todo(); // action = new ShowInPackageViewAction(this); // action.setActionDefinitionId(IJavaEditorActionDefinitionIds.SHOW_IN_PACKAGE_VIEW); // setAction("ShowInPackageView", action); //$NON-NLS-1$ // replace cut/copy paste actions with a version that implement 'add imports // on paste' action = new ClipboardOperationAction( DartEditorMessages.getBundleForConstructedKeys(), "Editor.Cut.", this, ITextOperationTarget.CUT); //$NON-NLS-1$ setAction(ITextEditorActionConstants.CUT, action); action = new ClipboardOperationAction( DartEditorMessages.getBundleForConstructedKeys(), "Editor.Copy.", this, ITextOperationTarget.COPY); //$NON-NLS-1$ setAction(ITextEditorActionConstants.COPY, action); action = new ClipboardOperationAction( DartEditorMessages.getBundleForConstructedKeys(), "Editor.Paste.", this, ITextOperationTarget.PASTE); //$NON-NLS-1$ setAction(ITextEditorActionConstants.PASTE, action); DartX.todo(); // action = new CopyQualifiedNameAction(this); // setAction(IDartEditorActionConstants.COPY_QUALIFIED_NAME, action); removeTrailingWhitespaceAction = new RemoveTrailingWhitespaceAction(getSourceViewer()); } @Override protected IVerticalRulerColumn createAnnotationRulerColumn(CompositeRuler ruler) { DartX.todo(); return super.createAnnotationRulerColumn(ruler); // if (!getPreferenceStore().getBoolean( // PreferenceConstants.EDITOR_ANNOTATION_ROLL_OVER)) // return super.createAnnotationRulerColumn(ruler); // // AnnotationRulerColumn column = new AnnotationRulerColumn( // VERTICAL_RULER_WIDTH, getAnnotationAccess()); // column.setHover(new JavaExpandHover(ruler, getAnnotationAccess(), // new IDoubleClickListener() { // // public void doubleClick(DoubleClickEvent event) { // // for now: just invoke ruler double click action // triggerAction(ITextEditorActionConstants.RULER_DOUBLE_CLICK); // } // // private void triggerAction(String actionID) { // IAction action = getAction(actionID); // if (action != null) { // if (action instanceof IUpdate) // ((IUpdate) action).update(); // // hack to propagate line change // if (action instanceof ISelectionListener) { // ((ISelectionListener) action).selectionChanged(null, null); // } // if (action.isEnabled()) // action.run(); // } // } // // })); // // return column; } @Override protected LineChangeHover createChangeHover() { return new DartChangeHover(DartPartitions.DART_PARTITIONING, getOrientation()); } protected ISourceViewer createDartSourceViewer(Composite parent, IVerticalRuler verticalRuler, IOverviewRuler overviewRuler, boolean isOverviewRulerVisible, int styles, IPreferenceStore store) { return new DartSourceViewer( parent, verticalRuler, getOverviewRuler(), isOverviewRulerVisible(), styles, store); } /** * Returns a new Dart source viewer configuration. * * @return a new <code>DartSourceViewerConfiguration</code> */ protected DartSourceViewerConfiguration createDartSourceViewerConfiguration() { DartTextTools textTools = DartToolsPlugin.getDefault().getDartTextTools(); return new DartSourceViewerConfiguration( textTools.getColorManager(), getPreferenceStore(), this, DartPartitions.DART_PARTITIONING); } @Override protected void createNavigationActions() { super.createNavigationActions(); final StyledText textWidget = getSourceViewer().getTextWidget(); IAction action = new SmartLineStartAction(textWidget, false); action.setActionDefinitionId(ITextEditorActionDefinitionIds.LINE_START); setAction(ITextEditorActionDefinitionIds.LINE_START, action); action = new SmartLineStartAction(textWidget, true); action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_LINE_START); setAction(ITextEditorActionDefinitionIds.SELECT_LINE_START, action); action = new NavigatePreviousSubWordAction(); action.setActionDefinitionId(ITextEditorActionDefinitionIds.WORD_PREVIOUS); setAction(ITextEditorActionDefinitionIds.WORD_PREVIOUS, action); textWidget.setKeyBinding(SWT.CTRL | SWT.ARROW_LEFT, SWT.NULL); action = new NavigateNextSubWordAction(); action.setActionDefinitionId(ITextEditorActionDefinitionIds.WORD_NEXT); setAction(ITextEditorActionDefinitionIds.WORD_NEXT, action); textWidget.setKeyBinding(SWT.CTRL | SWT.ARROW_RIGHT, SWT.NULL); action = new SelectPreviousSubWordAction(); action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS); setAction(ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS, action); textWidget.setKeyBinding(SWT.CTRL | SWT.SHIFT | SWT.ARROW_LEFT, SWT.NULL); action = new SelectNextSubWordAction(); action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_WORD_NEXT); setAction(ITextEditorActionDefinitionIds.SELECT_WORD_NEXT, action); textWidget.setKeyBinding(SWT.CTRL | SWT.SHIFT | SWT.ARROW_RIGHT, SWT.NULL); } /** * Creates the outline page used with this editor. * * @return the created Dart outline page */ protected DartOutlinePage_NEW createOutlinePage_NEW() { DartOutlinePage_NEW page = new DartOutlinePage_NEW(fOutlinerContextMenuId, this); setOutlinePageInput_NEW(page, getEditorInput()); return page; } /** * Creates the outline page used with this editor. * * @return the created Dart outline page */ protected DartOutlinePage createOutlinePage_OLD() { DartOutlinePage page = new DartOutlinePage(fOutlinerContextMenuId, this); setOutlinePageInput_OLD(page, getEditorInput()); return page; } @Override protected final ISourceViewer createSourceViewer(Composite parent, IVerticalRuler verticalRuler, int styles) { IPreferenceStore store = getPreferenceStore(); final ISourceViewer viewer = createDartSourceViewer( parent, verticalRuler, getOverviewRuler(), isOverviewRulerVisible(), styles, store); DartUIHelp.setHelp(this, viewer.getTextWidget(), DartHelpContextIds.JAVA_EDITOR); DartSourceViewer dartSourceViewer = null; if (viewer instanceof DartSourceViewer) { dartSourceViewer = (DartSourceViewer) viewer; } /* * This is a performance optimization to reduce the computation of the text presentation * triggered by {@link #setVisibleDocument(IDocument)} */ if (dartSourceViewer != null && isFoldingEnabled() && (store == null || !store.getBoolean(PreferenceConstants.EDITOR_SHOW_SEGMENTS))) { dartSourceViewer.prepareDelayedProjection(); } 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$ DartX.todo("hover"); fProjectionSupport.setHoverControlCreator(new IInformationControlCreator() { @Override public IInformationControl createInformationControl(Shell shell) { return new SourceViewerInformationControl( shell, SWT.TOOL | SWT.NO_TRIM | getOrientation(), SWT.NONE, EditorsUI.getTooltipAffordanceString()); } }); fProjectionSupport.setInformationPresenterControlCreator(new IInformationControlCreator() { @Override public IInformationControl createInformationControl(Shell shell) { int shellStyle = SWT.RESIZE | SWT.TOOL | getOrientation(); int style = SWT.V_SCROLL | SWT.H_SCROLL; return new SourceViewerInformationControl(shell, shellStyle, style); } }); fProjectionSupport.install(); fProjectionModelUpdater = DartToolsPlugin.getDefault().getFoldingStructureProviderRegistry().getCurrentFoldingProvider(); if (fProjectionModelUpdater != null) { fProjectionModelUpdater.install(this, projectionViewer); } // ensure source viewer decoration support has been created and configured getSourceViewerDecorationSupport(viewer); // track text selection range viewer.getTextWidget().addCaretListener(new CaretListener() { @Override public void caretMoved(CaretEvent event) { textSelectionRange = SourceRangeFactory.forStartLength(event.caretOffset, 0); } }); viewer.getTextWidget().addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { textSelectionRange = SourceRangeFactory.forStartEnd(event.x, event.y); } }); EditorUtility.addGTKPasteHack(viewer); return viewer; } protected void doSelectionChanged(ISelection selection) { if (selection instanceof IStructuredSelection) { IStructuredSelection structuredSelection = (IStructuredSelection) selection; for (Object selectionObject : structuredSelection.toList()) { if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) { if (selectionObject instanceof Outline) { setSelection_NEW((Outline) selectionObject, !isActivePart()); break; } } else { if (selectionObject instanceof LightNodeElement) { setSelection_OLD((LightNodeElement) selectionObject, !isActivePart()); break; } } } } } @Override protected void doSetInput(IEditorInput input) throws CoreException { inputResourceFile = null; inputJavaFile = null; if (input instanceof IFileEditorInput) { IFileEditorInput fileInput = (IFileEditorInput) input; inputResourceFile = fileInput.getFile(); } else if (input instanceof IURIEditorInput) { IURIEditorInput uriInput = (IURIEditorInput) input; URI uri = uriInput.getURI(); if (uri != null && uri.getScheme().equals("file") && uri.isAbsolute()) { try { inputJavaFile = new File(uri); } catch (Throwable e) { } } } uninstallSemanticHighlighting(); // TODO(scheglov) This code is intentionally left commented. // If we don't follow the "!(sourceViewer instanceof ISourceViewerExtension2)", // it causes https://code.google.com/p/dart/issues/detail?id=18297 // To reproduce it, open editor, rename input file and try to use completion or Quick Assist. // So, we just do it always. { setPreferenceStore(createCombinedPreferenceStore(input)); internalDoSetInput(input); } installSemanticHighlighting(); // ISourceViewer sourceViewer = getSourceViewer(); // if (!(sourceViewer instanceof ISourceViewerExtension2)) { // setPreferenceStore(createCombinedPreferenceStore(input)); // internalDoSetInput(input); // return; // } // // // uninstall & unregister preference store listener // getSourceViewerDecorationSupport(sourceViewer).uninstall(); // ((ISourceViewerExtension2) sourceViewer).unconfigure(); // // setPreferenceStore(createCombinedPreferenceStore(input)); // // // install & register preference store listener // sourceViewer.configure(getSourceViewerConfiguration()); // getSourceViewerDecorationSupport(sourceViewer).install(getPreferenceStore()); // // internalDoSetInput(input); } @Override protected void doSetSelection(ISelection selection) { super.doSetSelection(selection); synchronizeOutlinePageSelection(); } /** * {@inheritDoc} * <p> * Overrides the default implementation to handle {@link IJavaAnnotation}. * </p> * * @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 */ @Override protected Annotation findAnnotation(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()); @SuppressWarnings("rawtypes") Iterator e = new DartAnnotationIterator(model, true, true); while (e.hasNext()) { Annotation a = (Annotation) e.next(); if (!isNavigationTarget(a)) { 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 = 0; if (forward) { currentDistance = p.getOffset() - offset; if (currentDistance < 0) { currentDistance = endOfDocument + currentDistance; } if (currentDistance < distance || currentDistance == distance && p.length < nextAnnotationPosition.length) { distance = currentDistance; nextAnnotation = a; nextAnnotationPosition = p; } } else { currentDistance = 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; } /** * Returns the standard action group of this editor. * * @return returns this editor's standard action group */ protected ActionGroup getActionGroup() { return fActionGroups; } /** * Returns the most narrow Dart element including the given offset. * * @param offset the offset inside of the requested element * @return the most narrow Dart element */ abstract protected Object /*Element*/getElementAt(int offset); /** * Returns the most narrow Dart element including the given offset. * * @param offset the offset inside of the requested element * @param reconcile <code>true</code> if editor input should be reconciled in advance * @return the most narrow element */ protected Object /*Element*/getElementAt(int offset, boolean reconcile) { return getElementAt(offset); } /** * Returns the folding action group, or <code>null</code> if there is none. * * @return the folding action group, or <code>null</code> if there is none */ protected FoldingActionGroup getFoldingActionGroup() { return fFoldingGroup; } protected ISelectionChangedListener getPatchedSelectionChangedListener() { if (patchedSelectionChangedListener == null) { patchedSelectionChangedListener = new ISelectionChangedListener() { private Runnable runnable = new Runnable() { @Override public void run() { if (isDisposed()) { return; } updateSelectionDependentActions(); } }; private Display display; @Override public void selectionChanged(SelectionChangedEvent event) { Display current = Display.getCurrent(); if (current != null) { // Don't execute asynchronously if we're in a thread that has a display. // Fix for: https://bugs.eclipse.org/bugs/show_bug.cgi?id=368354 (the rationale // is that the actions were not being enabled because they were previously // updated in an async call). // but just patching getSelectionChangedListener() properly. runnable.run(); } else { if (display == null) { display = getSite().getShell().getDisplay(); } display.asyncExec(runnable); } handleCursorPositionChanged(); } }; } return patchedSelectionChangedListener; } /** * 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(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); } @Override protected String getStatusBanner(IStatus status) { if (fEncodingSupport != null) { String message = fEncodingSupport.getStatusBanner(status); if (message != null) { return message; } } return super.getStatusBanner(status); } @Override protected String getStatusHeader(IStatus status) { if (fEncodingSupport != null) { String message = fEncodingSupport.getStatusHeader(status); if (message != null) { return message; } } return super.getStatusHeader(status); } @Override protected String getStatusMessage(IStatus status) { if (fEncodingSupport != null) { String message = fEncodingSupport.getStatusMessage(status); if (message != null) { return message; } } return super.getStatusMessage(status); } @Override protected IOperationApprover getUndoRedoOperationApprover(IUndoContext undoContext) { return new NonLocalUndoUserApprover( undoContext, this, new Object[] {inputResourceFile}, IResource.class); } @Override protected void handleCursorPositionChanged() { super.handleCursorPositionChanged(); fCachedSelectedRange = getViewer().getSelectedRange(); } @Override protected void handlePreferenceStoreChanged(PropertyChangeEvent event) { String property = event.getProperty(); try { ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer == null) { return; } if (AbstractDecoratedTextEditorPreferenceConstants.EDITOR_TAB_WIDTH.equals(property)) { IPreferenceStore store = getPreferenceStore(); if (store != null) { sourceViewer.getTextWidget().setTabs( store.getInt(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_TAB_WIDTH)); } if (isTabsToSpacesConversionEnabled()) { uninstallTabsToSpacesConverter(); installTabsToSpacesConverter(); } return; } if (isEditorHoverProperty(property)) { updateHoverBehavior(); } boolean newBooleanValue = false; Object newValue = event.getNewValue(); if (newValue != null) { newBooleanValue = Boolean.valueOf(newValue.toString()).booleanValue(); } if (PreferenceConstants.EDITOR_SYNC_OUTLINE_ON_CURSOR_MOVE.equals(property)) { if (newBooleanValue) { selectionChanged(); } return; } if (PreferenceConstants.EDITOR_MARK_OCCURRENCES.equals(property)) { if (!newBooleanValue) { uninstallOccurrencesFinder(); } else { installOccurrencesFinder(true); } return; } ((DartSourceViewerConfiguration) getSourceViewerConfiguration()).handlePropertyChangeEvent(event); if (affectsOverrideIndicatorAnnotations(event)) { if (isShowingOverrideIndicators()) { if (fOverrideIndicatorManager == null) { installOverrideIndicator(true); } } else { if (fOverrideIndicatorManager != null) { uninstallOverrideIndicator(); } } return; } if (PreferenceConstants.EDITOR_FOLDING_PROVIDER.equals(property)) { if (sourceViewer instanceof ProjectionViewer) { DartX.todo("folding"); ProjectionViewer projectionViewer = (ProjectionViewer) sourceViewer; if (fProjectionModelUpdater != null) { fProjectionModelUpdater.uninstall(); } // either freshly enabled or provider changed fProjectionModelUpdater = DartToolsPlugin.getDefault().getFoldingStructureProviderRegistry().getCurrentFoldingProvider(); if (fProjectionModelUpdater != null) { fProjectionModelUpdater.install(this, projectionViewer); } } return; } if (DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE.equals(property) || DefaultCodeFormatterConstants.FORMATTER_INDENTATION_SIZE.equals(property) || DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR.equals(property)) { StyledText textWidget = sourceViewer.getTextWidget(); int tabWidth = getSourceViewerConfiguration().getTabWidth(sourceViewer); if (textWidget.getTabs() != tabWidth) { textWidget.setTabs(tabWidth); } return; } if (PreferenceConstants.EDITOR_FOLDING_ENABLED.equals(property)) { if (sourceViewer instanceof ProjectionViewer) { new ToggleFoldingRunner().runWhenNextVisible(); } return; } } finally { super.handlePreferenceStoreChanged(event); } if (AbstractDecoratedTextEditorPreferenceConstants.SHOW_RANGE_INDICATOR.equals(property)) { // superclass already installed the range indicator Object newValue = event.getNewValue(); ISourceViewer viewer = getSourceViewer(); if (newValue != null && viewer != null) { if (Boolean.valueOf(newValue.toString()).booleanValue()) { // adjust the highlightrange in order to get the magnet right after // changing the selection Point selection = viewer.getSelectedRange(); adjustHighlightRange(selection.x, selection.y); } } } } @Override protected void initializeDragAndDrop(ISourceViewer viewer) { // Disabled in Dart Editor. // super.initializeDragAndDrop(viewer); } @Override protected void initializeEditor() { IPreferenceStore store = createCombinedPreferenceStore(null); setPreferenceStore(store); setSourceViewerConfiguration(createDartSourceViewerConfiguration()); } @Override protected void initializeKeyBindingScopes() { setKeyBindingScopes(new String[] {"com.google.dart.tools.ui.dartViewScope"}); //$NON-NLS-1$ } /** * Initializes the given viewer's colors. * * @param viewer the viewer to be initialized */ @Override protected void initializeViewerColors(ISourceViewer viewer) { // is handled by DartSourceViewer } /** * Installs the encoding support on the given text editor. * <p> * Subclasses may override to install their own encoding support or to disable the default * encoding support. * </p> */ protected void installEncodingSupport() { fEncodingSupport = new DefaultEncodingSupport(); fEncodingSupport.initialize(this); } protected void installOccurrencesFinder(boolean forceUpdate) { DartSourceViewer viewer = (DartSourceViewer) getSourceViewer(); if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) { occurrencesManager_NEW = new MarkOccurrencesManager_NEW( this, viewer, getDartReconcilingStrategy()); } else { occurrencesManager_OLD = new MarkOccurrencesManager_OLD(this, viewer); } } protected void installOverrideIndicator(boolean provideAST) { uninstallOverrideIndicator(); IAnnotationModel model = getDocumentProvider().getAnnotationModel(getEditorInput()); if (model == null) { return; } if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) { fOverrideIndicatorManager = new OverrideIndicatorManager(model); } else { com.google.dart.engine.ast.CompilationUnit cu = getInputUnit(); fOverrideIndicatorManager = new OverrideIndicatorManager(model, cu); } fOverrideIndicatorManager.install(this); } protected boolean isActivePart() { IWorkbenchPart part = getActivePart(); return part != null && part.equals(this); } protected boolean isMarkingOccurrences() { IPreferenceStore store = getPreferenceStore(); return store != null && store.getBoolean(PreferenceConstants.EDITOR_MARK_OCCURRENCES); } /** * Tells whether override indicators are shown. * * @return <code>true</code> if the override indicators are shown */ protected boolean isShowingOverrideIndicators() { AnnotationPreference preference = getAnnotationPreferenceLookup().getAnnotationPreference( OverrideIndicatorManager.ANNOTATION_TYPE); IPreferenceStore store = getPreferenceStore(); return getBoolean(store, preference.getHighlightPreferenceKey()) || getBoolean(store, preference.getVerticalRulerPreferenceKey()) || getBoolean(store, preference.getOverviewRulerPreferenceKey()) || getBoolean(store, preference.getTextPreferenceKey()); } @Override protected boolean isTabsToSpacesConversionEnabled() { return getPreferenceStore() != null && getPreferenceStore().getBoolean( AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SPACES_FOR_TABS); } @Override protected void performRevert() { ProjectionViewer projectionViewer = (ProjectionViewer) getSourceViewer(); projectionViewer.setRedraw(false); try { boolean projectionMode = projectionViewer.isProjectionMode(); if (projectionMode) { projectionViewer.disableProjection(); DartX.todo("folding"); if (fProjectionModelUpdater != null) { fProjectionModelUpdater.uninstall(); } } super.performRevert(); if (projectionMode) { DartX.todo("folding"); if (fProjectionModelUpdater != null) { fProjectionModelUpdater.install(this, projectionViewer); } projectionViewer.enableProjection(); } } finally { projectionViewer.setRedraw(true); checkEditableState(); } } @Override protected void performSave(boolean overwrite, IProgressMonitor progressMonitor) { UIInstrumentationBuilder instrumentation = UIInstrumentation.builder("Editor-Save-Perf"); try { performSaveActions(); instrumentation.metric("Save-Actions", "complete"); super.performSave(overwrite, progressMonitor); instrumentation.metric("Save", "complete"); int lines = getDocumentProvider().getDocument(getEditorInput()).getNumberOfLines(); instrumentation.metric("Lines", lines); } finally { instrumentation.log(); } } @Override protected void rulerContextMenuAboutToShow(IMenuManager menu) { super.rulerContextMenuAboutToShow(menu); // Need to remove preferences item from the list; can't supress it via activities // because there is no command defined for this item menu.remove(ITextEditorActionConstants.RULER_PREFERENCES); IAction action = getAction("FoldingToggle"); //$NON-NLS-1$ menu.add(action); action = getAction("FoldingExpandAll"); //$NON-NLS-1$ menu.add(action); action = getAction("FoldingCollapseAll"); //$NON-NLS-1$ menu.add(action); action = getAction("FoldingRestore"); //$NON-NLS-1$ menu.add(action); } /** * React to changed selection. */ protected void selectionChanged() { if (getSelectionProvider() == null) { return; } ISelection selection = getSelectionProvider().getSelection(); // TODO(scheglov) LightNodeElement element = computeHighlightRangeSourceElement( resolvedUnit, ((ITextSelection) selection).getOffset()); setSelectionRange(element, false); if (!fSelectionChangedViaGotoAnnotation) { updateStatusLine(); } fSelectionChangedViaGotoAnnotation = false; } protected void setContextMenuContext(IMenuManager menu, ActionContext context) { fOpenEditorActionGroup.setContext(context); fOpenEditorActionGroup.fillContextMenu(menu); fOpenEditorActionGroup.setContext(null); } /** * Sets the input of the editor's outline page. * * @param page the Dart outline page * @param input the editor input */ protected void setOutlinePageInput_NEW(DartOutlinePage_NEW page, IEditorInput input) { // NOOP } /** * Sets the input of the editor's outline page. * * @param page the Dart outline page * @param input the editor input */ protected void setOutlinePageInput_OLD(DartOutlinePage page, IEditorInput input) { if (page == null) { return; } page.setInput(resolvedUnit); } /** * Sets the outliner's context menu ID. * * @param menuId the menu ID */ protected void setOutlinerContextMenuId(String menuId) { fOutlinerContextMenuId = menuId; } @Override protected void setPreferenceStore(IPreferenceStore store) { super.setPreferenceStore(store); if (getSourceViewerConfiguration() instanceof DartSourceViewerConfiguration) { DartTextTools textTools = DartToolsPlugin.getDefault().getDartTextTools(); setSourceViewerConfiguration(new DartSourceViewerConfiguration( textTools.getColorManager(), store, this, DartPartitions.DART_PARTITIONING)); } if (getSourceViewer() instanceof DartSourceViewer) { ((DartSourceViewer) getSourceViewer()).setPreferenceStore(store); } } protected void setSelection(SourceReference reference, boolean moveCursor) { // NOTUSED if (getSelectionProvider() == null) { return; } ISelection selection = getSelectionProvider().getSelection(); if (selection instanceof ITextSelection) { ITextSelection textSelection = (ITextSelection) selection; // PR 39995: [navigation] Forward history cleared after going back in // navigation history: // mark only in navigation history if the cursor is being moved (which it // isn't if // this is called from a PostSelectionEvent that should only update the // magnet) if (moveCursor && (textSelection.getOffset() != 0 || textSelection.getLength() != 0)) { markInNavigationHistory(); } } if (reference != null) { StyledText textWidget = null; ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer != null) { textWidget = sourceViewer.getTextWidget(); } if (textWidget == null) { return; } try { SourceRange 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; range = 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 (DartModelException x) { } catch (IllegalArgumentException x) { } } else if (moveCursor) { resetHighlightRange(); markInNavigationHistory(); } } protected void setSelectionRange(LightNodeElement reference, boolean moveCursor) { if (getSelectionProvider() == null) { return; } ISelection selection = getSelectionProvider().getSelection(); if (selection instanceof ITextSelection) { ITextSelection textSelection = (ITextSelection) selection; // PR 39995: [navigation] Forward history cleared after going back in navigation history: // mark only in navigation history if the cursor is being moved (which it isn't if // this is called from a PostSelectionEvent that should only update the magnet) if (moveCursor && (textSelection.getOffset() != 0 || textSelection.getLength() != 0)) { markInNavigationHistory(); } } if (reference != null) { StyledText textWidget = null; ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer != null) { textWidget = sourceViewer.getTextWidget(); } if (textWidget == null) { return; } try { int offset = reference.getNode().getOffset(); int length = reference.getNode().getLength(); if (offset < 0 || length < 0) { return; } setHighlightRange(offset, length, moveCursor); if (!moveCursor) { return; } offset = -1; length = -1; offset = reference.getNameOffset(); length = reference.getNameLength(); if (offset > -1 && length > 0) { try { textWidget.setRedraw(false); sourceViewer.revealRange(offset, length); sourceViewer.setSelectedRange(offset, length); } finally { textWidget.setRedraw(true); } markInNavigationHistory(); } } catch (IllegalArgumentException x) { } } else if (moveCursor) { resetHighlightRange(); markInNavigationHistory(); } } /** * Synchronizes the outliner selection with the given element position in the editor. * * @param element the dart element to select */ protected void synchronizeOutlinePage(Object /* Element */element) { synchronizeOutlinePage(element, true); } /** * Synchronizes the outliner selection with the given element position in the editor. * * @param element the Dart element to select * @param checkIfOutlinePageActive <code>true</code> if check for active outline page needs to be * done */ protected void synchronizeOutlinePage(Object /* ASTNode */element, boolean checkIfOutlinePageActive) { if (fOutlinePage_OLD != null && element != null && !(checkIfOutlinePageActive && isOutlinePageActive())) { // TODO(scheglov) // ((DartOutlinePage) fOutlinePage).select((LightNodeElement) element); // ((DartOutlinePage) fOutlinePage).setInput( // (com.google.dart.engine.ast.CompilationUnit) ((LightNodeElement) element).getNode().getRoot(), // (LightNodeElement) element); } } protected void uninstallOccurrencesFinder() { if (occurrencesManager_OLD != null) { occurrencesManager_OLD.dispose(); occurrencesManager_OLD = null; } if (occurrencesManager_NEW != null) { occurrencesManager_NEW.dispose(); occurrencesManager_NEW = null; } } protected void uninstallOverrideIndicator() { if (fOverrideIndicatorManager != null) { fOverrideIndicatorManager.removeAnnotations(); fOverrideIndicatorManager.uninstall(); fOverrideIndicatorManager = null; } } @Override protected void updateMarkerViews(Annotation annotation) { super.updateMarkerViews(annotation); } @Override protected void updatePropertyDependentActions() { super.updatePropertyDependentActions(); if (fEncodingSupport != null) { fEncodingSupport.reset(); } } protected void updateStatusLine() { ITextSelection selection = (ITextSelection) getSelectionProvider().getSelection(); Annotation annotation = getAnnotation(selection.getOffset(), selection.getLength()); setStatusLineErrorMessage(null); setStatusLineMessage(null); if (annotation != null) { updateMarkerViews(annotation); } } boolean isFoldingEnabled() { return DartToolsPlugin.getDefault().getPreferenceStore().getBoolean( PreferenceConstants.EDITOR_FOLDING_ENABLED); } boolean markOccurrencesOfType(/* IBinding */Object binding) { DartX.todo("modifiers,marking"); if (binding == null) { return false; } // int kind = binding.getKind(); // // if (fMarkTypeOccurrences && kind == IBinding.TYPE) // return true; // // if (fMarkMethodOccurrences && kind == IBinding.METHOD) // return true; // // if (kind == IBinding.VARIABLE) { // IVariableBinding variableBinding = (IVariableBinding) binding; // if (variableBinding.isField()) { // int constantModifier = IModifierConstants.ACC_STATIC // | IModifierConstants.ACC_FINAL; // boolean isConstant = (variableBinding.getModifiers() & constantModifier) == constantModifier; // if (isConstant) // return fMarkConstantOccurrences; // else // return fMarkFieldOccurrences; // } // // return fMarkLocalVariableypeOccurrences; // } return false; } /** * Applies the current selection to the Outline view. */ private void applySelectionToOutline() { // prepare selection ISelection selection = getSelectionProvider().getSelection(); if (!(selection instanceof TextSelection)) { return; } int offset = ((TextSelection) selection).getOffset(); // apply selected element if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) { if (fOutlinePage_NEW != null) { fOutlinePage_NEW.select(offset); } } else { // prepare resolved unit com.google.dart.engine.ast.CompilationUnit unit = resolvedUnit; if (unit == null) { return; } // apply selected element LightNodeElement element = computeHighlightRangeSourceElement(resolvedUnit, offset); if (element != null && fOutlinePage_OLD != null) { fOutlinePage_OLD.select(element); } } } /** * Creates and returns the preference store for this Dart editor with the given input. * * @param input The editor input for which to create the preference store * @return the preference store for this editor */ private IPreferenceStore createCombinedPreferenceStore(IEditorInput input) { List<IPreferenceStore> stores = new ArrayList<IPreferenceStore>(3); IProject project = EditorUtility.getProject(input); if (project != null) { stores.add(new EclipsePreferencesAdapter(new ProjectScope(project), DartCore.PLUGIN_ID)); } stores.add(DartToolsPlugin.getDefault().getPreferenceStore()); stores.add(new PreferencesAdapter(DartCore.getPlugin().getPluginPreferences())); stores.add(EditorsUI.getPreferenceStore()); return new ChainedPreferenceStore(stores.toArray(new IPreferenceStore[stores.size()])); } /** * Notifies all listeners of {@link DartSelection}. */ private void fireDartSelectionListeners() { ISelection selection = getSelectionProvider().getSelection(); SelectionChangedEvent event = new SelectionChangedEvent(selectionProvider, selection); for (ISelectionChangedListener listener : ImmutableList.copyOf(dartSelectionListeners)) { listener.selectionChanged(event); } } private IWorkbenchPart getActivePart() { IWorkbenchWindow window = getSite().getWorkbenchWindow(); IPartService service = window.getPartService(); IWorkbenchPart part = service.getActivePart(); return part; } /** * Returns the annotation overlapping with the given range or <code>null</code>. * * @param offset the region offset * @param length the region length * @return the found annotation or <code>null</code> */ private Annotation getAnnotation(int offset, int length) { IAnnotationModel model = getDocumentProvider().getAnnotationModel(getEditorInput()); @SuppressWarnings("rawtypes") Iterator e = new DartAnnotationIterator(model, true, false); while (e.hasNext()) { Annotation a = (Annotation) e.next(); Position p = model.getPosition(a); if (p != null && p.overlapsWith(offset, length)) { return a; } } return null; } /** * @return the {@link AssistContext} with resolved {@link CompilationUnit}, selection and * {@link SearchEngine}. May be <code>null</code>. */ private AssistContext getAssistContext(ITextSelection textSelection) { try { // prepare selection if (textSelection == null) { return null; } int selectionOffset = 0; int selectionLength = 0; { selectionOffset = textSelection.getOffset(); selectionLength = textSelection.getLength(); } // TODO(scheglov) Analysis Server if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) { String file = getInputFilePath(); if (file == null) { return null; } return new AssistContext(null, null, file, null, null, selectionOffset, selectionLength); } // prepare source Source source = getInputSource(); if (source == null) { return null; } // prepare AnalysisContext AnalysisContext analysisContext = getInputAnalysisContext(); if (analysisContext == null) { return null; } // prepare input CompilationUnit com.google.dart.engine.ast.CompilationUnit unit = getInputUnit(); if (unit == null) { return null; } if (unit.getElement() == null) { return null; } // return AssistContext Index index = DartCore.getProjectManager().getIndex(); return new AssistContext( SearchEngineFactory.createSearchEngine(index), analysisContext, null, source, unit, selectionOffset, selectionLength); } catch (Throwable e) { throw new Error(e); } } /** * Returns the boolean preference for the given key. * * @param store the preference store * @param key the preference key * @return <code>true</code> if the key exists in the store and its value is <code>true</code> */ private boolean getBoolean(IPreferenceStore store, String key) { return key != null && store.getBoolean(key); } /** * Install Semantic Highlighting. */ private void installSemanticHighlighting() { DartSourceViewer viewer = (DartSourceViewer) getSourceViewer(); if (viewer == null) { return; } // TODO(scheglov) Analysis Server: remove this old method if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) { AnalysisServerData analysisServerData = DartCore.getAnalysisServerData(); String file = getInputFilePath(); if (file != null) { analysisServerHighlightManager = new SemanticHighlightingManager_NEW( viewer, file, getDartReconcilingStrategy()); } return; } if (fSemanticManager == null) { fSemanticManager = new SemanticHighlightingManager(); fSemanticManager.install( this, viewer, DartToolsPlugin.getDefault().getDartTextTools().getColorManager(), getPreferenceStore()); } } private void internalDoSetInput(IEditorInput input) throws CoreException { ISourceViewer sourceViewer = getSourceViewer(); DartSourceViewer dartSourceViewer = null; if (sourceViewer instanceof DartSourceViewer) { dartSourceViewer = (DartSourceViewer) sourceViewer; } IPreferenceStore store = getPreferenceStore(); if (dartSourceViewer != null && isFoldingEnabled() && (store == null || !store.getBoolean(PreferenceConstants.EDITOR_SHOW_SEGMENTS))) { dartSourceViewer.prepareDelayedProjection(); } try { super.doSetInput(input); } catch (Throwable e) { @SuppressWarnings("restriction") IEditorInput dumyInput = new org.eclipse.ui.internal.part.NullEditorInput(); super.doSetInput(dumyInput); close(false); return; } if (dartSourceViewer != null && dartSourceViewer.getReconciler() == null) { IReconciler reconciler = getSourceViewerConfiguration().getReconciler(dartSourceViewer); if (reconciler != null) { reconciler.install(dartSourceViewer); dartSourceViewer.setReconciler(reconciler); } } if (fEncodingSupport != null) { fEncodingSupport.reset(); } if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) { AnalysisServerData analysisServerData = DartCore.getAnalysisServerData(); String file = getInputFilePath(); if (file != null) { analysisServerData.addNavigationListener(file, navigationListener); analysisServerData.addOutlineListener(file, outlineListener); } } if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) { setOutlinePageInput_NEW(fOutlinePage_NEW, input); } else { setOutlinePageInput_OLD(fOutlinePage_OLD, input); } if (isShowingOverrideIndicators()) { installOverrideIndicator(false); } } /* * Tells whether the content is editable. */ private boolean isContentEditable() { if (PubCacheManager_NEW.isPubCacheResource(inputResourceFile)) { return false; } return inputResourceFile != null && !DartCore.isContainedInPackages(inputResourceFile) && !inputResourceFile.isReadOnly(); } /** * @return {@code true} if this {@link DartEditor} was already disposed. */ private boolean isDisposed() { return getSourceViewer() == null; } private boolean isEditorHoverProperty(String property) { return PreferenceConstants.EDITOR_TEXT_HOVER_MODIFIERS.equals(property); } private boolean isFormatOnSaveEnabled() { return PreferenceConstants.getPreferenceStore().getBoolean( PreferenceConstants.EDITOR_FORMAT_ON_SAVES); } private boolean isOutlinePageActive() { IWorkbenchPart part = getActivePart(); if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) { return part instanceof ContentOutline && ((ContentOutline) part).getCurrentPage() == fOutlinePage_NEW; } else { return part instanceof ContentOutline && ((ContentOutline) part).getCurrentPage() == fOutlinePage_OLD; } } private boolean isRemoveTrailingWhitespaceEnabled() { return PreferenceConstants.getPreferenceStore().getBoolean( PreferenceConstants.EDITOR_REMOVE_TRAILING_WS); } private void patchSelectionChangeParticipation() { // To address dartbug.com/7998 //TODO (pquitslund): revisit when we move to 3.8 and see if this hack is still needed getSelectionProvider().removeSelectionChangedListener(getSelectionChangedListener()); getSelectionProvider().addSelectionChangedListener(getPatchedSelectionChangedListener()); } private void performSaveActions() { if (isRemoveTrailingWhitespaceEnabled()) { try { removeTrailingWhitespaceAction.run(); } catch (InvocationTargetException e) { DartToolsPlugin.log(e); } } if (isFormatOnSaveEnabled()) { new FormatElementAction(true).run(); } } /** * Uninstall Semantic Highlighting. */ private void uninstallSemanticHighlighting() { // TODO(scheglov) Analysis Server: remove this old method if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) { if (analysisServerHighlightManager != null) { analysisServerHighlightManager.dispose(); analysisServerHighlightManager = null; } return; } if (fSemanticManager != null) { fSemanticManager.uninstall(); fSemanticManager = null; } } /* * Update the hovering behavior depending on the preferences. */ private void updateHoverBehavior() { SourceViewerConfiguration configuration = getSourceViewerConfiguration(); String[] types = configuration.getConfiguredContentTypes(getSourceViewer()); for (int i = 0; i < types.length; i++) { String t = types[i]; ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer instanceof ITextViewerExtension2) { // Remove existing hovers ((ITextViewerExtension2) sourceViewer).removeTextHovers(t); int[] stateMasks = configuration.getConfiguredTextHoverStateMasks(getSourceViewer(), t); if (stateMasks != null) { for (int j = 0; j < stateMasks.length; j++) { int stateMask = stateMasks[j]; ITextHover textHover = configuration.getTextHover(sourceViewer, t, stateMask); ((ITextViewerExtension2) sourceViewer).setTextHover(textHover, t, stateMask); } } else { ITextHover textHover = configuration.getTextHover(sourceViewer, t); ((ITextViewerExtension2) sourceViewer).setTextHover( textHover, t, ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK); } } else { sourceViewer.setTextHover(configuration.getTextHover(sourceViewer, t), t); } } } private void updateOutlinePageNew(Outline outline) { if (fOutlinePage_NEW != null && outline != null) { int offset = getSourceViewer().getSelectedRange().x; fOutlinePage_NEW.setInput(outline, offset); } } }