| /******************************************************************************* |
| * Copyright (c) 2005, 2016 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * QNX Software System |
| * Anton Leherbauer (Wind River Systems) |
| * Markus Schorn (Wind River Systems) |
| * Sergey Prigogin (Google) |
| * Axel Mueller - [289339] Surround with |
| * Tomasz Wesolowski - [320561] Override indicators |
| * Serge Beauchamp (Freescale Semiconductor) - Bug 417909 |
| *******************************************************************************/ |
| package org.eclipse.cdt.internal.ui.editor; |
| |
| import java.text.CharacterIterator; |
| import java.util.ArrayDeque; |
| import java.util.ArrayList; |
| import java.util.Deque; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.ResourceBundle; |
| import java.util.Set; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.resources.ProjectScope; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.ListenerList; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.content.IContentType; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.core.runtime.preferences.IPreferencesService; |
| import org.eclipse.help.IContext; |
| import org.eclipse.help.IContextProvider; |
| import org.eclipse.jface.action.GroupMarker; |
| import org.eclipse.jface.action.IAction; |
| import org.eclipse.jface.action.IMenuManager; |
| import org.eclipse.jface.action.IStatusLineManager; |
| import org.eclipse.jface.action.MenuManager; |
| import org.eclipse.jface.action.Separator; |
| import org.eclipse.jface.dialogs.Dialog; |
| import org.eclipse.jface.dialogs.IDialogConstants; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.dialogs.MessageDialogWithToggle; |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.jface.preference.PreferenceDialog; |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.BadPositionCategoryException; |
| import org.eclipse.jface.text.DefaultLineTracker; |
| import org.eclipse.jface.text.Document; |
| import org.eclipse.jface.text.DocumentEvent; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.IDocumentExtension; |
| import org.eclipse.jface.text.IDocumentExtension4; |
| import org.eclipse.jface.text.IDocumentListener; |
| import org.eclipse.jface.text.IInformationControl; |
| import org.eclipse.jface.text.IInformationControlCreator; |
| import org.eclipse.jface.text.IPositionUpdater; |
| import org.eclipse.jface.text.IRegion; |
| import org.eclipse.jface.text.ISelectionValidator; |
| import org.eclipse.jface.text.ISynchronizable; |
| import org.eclipse.jface.text.ITextHover; |
| import org.eclipse.jface.text.ITextInputListener; |
| import org.eclipse.jface.text.ITextSelection; |
| import org.eclipse.jface.text.ITextViewer; |
| import org.eclipse.jface.text.ITextViewerExtension; |
| import org.eclipse.jface.text.ITextViewerExtension2; |
| import org.eclipse.jface.text.ITextViewerExtension5; |
| import org.eclipse.jface.text.ITextViewerExtension7; |
| import org.eclipse.jface.text.ITypedRegion; |
| import org.eclipse.jface.text.IWidgetTokenKeeper; |
| import org.eclipse.jface.text.Position; |
| import org.eclipse.jface.text.Region; |
| import org.eclipse.jface.text.TextUtilities; |
| import org.eclipse.jface.text.contentassist.ContentAssistant; |
| import org.eclipse.jface.text.contentassist.IContentAssistant; |
| import org.eclipse.jface.text.formatter.FormattingContext; |
| import org.eclipse.jface.text.formatter.FormattingContextProperties; |
| import org.eclipse.jface.text.formatter.IFormattingContext; |
| import org.eclipse.jface.text.link.ILinkedModeListener; |
| import org.eclipse.jface.text.link.LinkedModeModel; |
| import org.eclipse.jface.text.link.LinkedModeUI; |
| import org.eclipse.jface.text.link.LinkedModeUI.ExitFlags; |
| import org.eclipse.jface.text.link.LinkedModeUI.IExitPolicy; |
| import org.eclipse.jface.text.link.LinkedPosition; |
| import org.eclipse.jface.text.link.LinkedPositionGroup; |
| import org.eclipse.jface.text.source.Annotation; |
| import org.eclipse.jface.text.source.AnnotationRulerColumn; |
| import org.eclipse.jface.text.source.CompositeRuler; |
| import org.eclipse.jface.text.source.IAnnotationModel; |
| import org.eclipse.jface.text.source.IAnnotationModelExtension; |
| import org.eclipse.jface.text.source.IAnnotationModelExtension2; |
| 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.ISourceViewerExtension2; |
| import org.eclipse.jface.text.source.IVerticalRuler; |
| import org.eclipse.jface.text.source.IVerticalRulerColumn; |
| import org.eclipse.jface.text.source.SourceViewerConfiguration; |
| import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; |
| import org.eclipse.jface.text.source.projection.ProjectionSupport; |
| import org.eclipse.jface.text.source.projection.ProjectionViewer; |
| import org.eclipse.jface.util.PropertyChangeEvent; |
| import org.eclipse.jface.viewers.DoubleClickEvent; |
| import org.eclipse.jface.viewers.IDoubleClickListener; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| import org.eclipse.jface.window.Window; |
| import org.eclipse.search.ui.actions.TextSearchGroup; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.BusyIndicator; |
| import org.eclipse.swt.custom.ST; |
| import org.eclipse.swt.custom.StyledText; |
| import org.eclipse.swt.custom.VerifyKeyListener; |
| import org.eclipse.swt.events.HelpEvent; |
| import org.eclipse.swt.events.HelpListener; |
| import org.eclipse.swt.events.VerifyEvent; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.ui.IActionBars; |
| import org.eclipse.ui.IEditorActionBarContributor; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.IFileEditorInput; |
| import org.eclipse.ui.IPageLayout; |
| import org.eclipse.ui.IPartService; |
| import org.eclipse.ui.ISelectionListener; |
| 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.dialogs.PreferencesUtil; |
| import org.eclipse.ui.editors.text.EditorsUI; |
| import org.eclipse.ui.editors.text.TextEditor; |
| import org.eclipse.ui.help.IWorkbenchHelpSystem; |
| import org.eclipse.ui.ide.IGotoMarker; |
| import org.eclipse.ui.navigator.ICommonMenuConstants; |
| import org.eclipse.ui.part.EditorActionBarContributor; |
| import org.eclipse.ui.part.IShowInSource; |
| import org.eclipse.ui.part.IShowInTargetList; |
| import org.eclipse.ui.part.ShowInContext; |
| import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants; |
| import org.eclipse.ui.texteditor.AbstractMarkerAnnotationModel; |
| import org.eclipse.ui.texteditor.AnnotationPreference; |
| import org.eclipse.ui.texteditor.ChainedPreferenceStore; |
| import org.eclipse.ui.texteditor.ContentAssistAction; |
| import org.eclipse.ui.texteditor.IDocumentProvider; |
| import org.eclipse.ui.texteditor.ITextEditorActionConstants; |
| import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; |
| import org.eclipse.ui.texteditor.IUpdate; |
| import org.eclipse.ui.texteditor.MarkerAnnotation; |
| import org.eclipse.ui.texteditor.MarkerUtilities; |
| import org.eclipse.ui.texteditor.SourceViewerDecorationSupport; |
| import org.eclipse.ui.texteditor.TextNavigationAction; |
| import org.eclipse.ui.texteditor.TextOperationAction; |
| import org.eclipse.ui.texteditor.link.EditorLinkedModeUI; |
| import org.eclipse.ui.texteditor.templates.ITemplatesPage; |
| import org.eclipse.ui.views.contentoutline.IContentOutlinePage; |
| |
| import com.ibm.icu.text.BreakIterator; |
| |
| import org.eclipse.cdt.core.CCorePlugin; |
| import org.eclipse.cdt.core.CCorePreferenceConstants; |
| import org.eclipse.cdt.core.dom.ast.IASTName; |
| import org.eclipse.cdt.core.dom.ast.IASTNodeSelector; |
| import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; |
| import org.eclipse.cdt.core.dom.ast.IBinding; |
| import org.eclipse.cdt.core.dom.ast.gnu.cpp.GPPLanguage; |
| import org.eclipse.cdt.core.formatter.DefaultCodeFormatterConstants; |
| import org.eclipse.cdt.core.model.CModelException; |
| import org.eclipse.cdt.core.model.ICElement; |
| import org.eclipse.cdt.core.model.ICProject; |
| import org.eclipse.cdt.core.model.ILanguage; |
| import org.eclipse.cdt.core.model.ISourceRange; |
| import org.eclipse.cdt.core.model.ISourceReference; |
| import org.eclipse.cdt.core.model.ITranslationUnit; |
| import org.eclipse.cdt.core.model.ITranslationUnitHolder; |
| import org.eclipse.cdt.core.model.IWorkingCopy; |
| import org.eclipse.cdt.ui.CUIPlugin; |
| import org.eclipse.cdt.ui.ICEditor; |
| import org.eclipse.cdt.ui.ICModelBasedEditor; |
| import org.eclipse.cdt.ui.IWorkingCopyManager; |
| import org.eclipse.cdt.ui.PreferenceConstants; |
| import org.eclipse.cdt.ui.actions.GenerateActionGroup; |
| import org.eclipse.cdt.ui.actions.OpenViewActionGroup; |
| import org.eclipse.cdt.ui.refactoring.actions.CRefactoringActionGroup; |
| import org.eclipse.cdt.ui.text.CSourceViewerConfiguration; |
| import org.eclipse.cdt.ui.text.ICPartitions; |
| import org.eclipse.cdt.ui.text.folding.ICFoldingStructureProvider; |
| |
| import org.eclipse.cdt.internal.core.model.ASTCache.ASTRunnable; |
| import org.eclipse.cdt.internal.corext.util.CodeFormatterUtil; |
| |
| import org.eclipse.cdt.internal.ui.CPluginImages; |
| import org.eclipse.cdt.internal.ui.ICHelpContextIds; |
| import org.eclipse.cdt.internal.ui.IContextMenuConstants; |
| import org.eclipse.cdt.internal.ui.actions.AddBlockCommentAction; |
| import org.eclipse.cdt.internal.ui.actions.AlignConstAction; |
| import org.eclipse.cdt.internal.ui.actions.FindWordAction; |
| import org.eclipse.cdt.internal.ui.actions.FoldingActionGroup; |
| import org.eclipse.cdt.internal.ui.actions.GoToNextPreviousMemberAction; |
| import org.eclipse.cdt.internal.ui.actions.GotoNextBookmarkAction; |
| import org.eclipse.cdt.internal.ui.actions.IndentAction; |
| import org.eclipse.cdt.internal.ui.actions.RemoveBlockCommentAction; |
| import org.eclipse.cdt.internal.ui.actions.StructureSelectEnclosingAction; |
| import org.eclipse.cdt.internal.ui.actions.StructureSelectHistoryAction; |
| import org.eclipse.cdt.internal.ui.actions.StructureSelectNextAction; |
| import org.eclipse.cdt.internal.ui.actions.StructureSelectPreviousAction; |
| import org.eclipse.cdt.internal.ui.actions.StructureSelectionAction; |
| import org.eclipse.cdt.internal.ui.actions.SurroundWithActionGroup; |
| import org.eclipse.cdt.internal.ui.dialogs.FormattingScopeDialog; |
| import org.eclipse.cdt.internal.ui.search.IOccurrencesFinder; |
| import org.eclipse.cdt.internal.ui.search.IOccurrencesFinder.OccurrenceLocation; |
| import org.eclipse.cdt.internal.ui.search.OccurrencesFinder; |
| import org.eclipse.cdt.internal.ui.search.actions.SelectionSearchGroup; |
| import org.eclipse.cdt.internal.ui.text.CHeuristicScanner; |
| import org.eclipse.cdt.internal.ui.text.CPairMatcher; |
| import org.eclipse.cdt.internal.ui.text.CSourceViewerScalableConfiguration; |
| import org.eclipse.cdt.internal.ui.text.CTextTools; |
| import org.eclipse.cdt.internal.ui.text.CWordFinder; |
| import org.eclipse.cdt.internal.ui.text.CWordIterator; |
| import org.eclipse.cdt.internal.ui.text.DocumentCharacterIterator; |
| import org.eclipse.cdt.internal.ui.text.ICReconcilingListener; |
| import org.eclipse.cdt.internal.ui.text.Symbols; |
| import org.eclipse.cdt.internal.ui.text.TabsToSpacesConverter; |
| import org.eclipse.cdt.internal.ui.text.c.hover.CExpandHover; |
| import org.eclipse.cdt.internal.ui.text.c.hover.SourceViewerInformationControl; |
| import org.eclipse.cdt.internal.ui.text.contentassist.ContentAssistPreference; |
| import org.eclipse.cdt.internal.ui.util.CUIHelp; |
| import org.eclipse.cdt.internal.ui.util.EditorUtility; |
| import org.eclipse.cdt.internal.ui.viewsupport.ISelectionListenerWithAST; |
| import org.eclipse.cdt.internal.ui.viewsupport.SelectionListenerWithASTManager; |
| |
| /** |
| * C/C++ source editor. |
| */ |
| public class CEditor extends TextEditor implements ICEditor, ISelectionChangedListener, ICReconcilingListener, |
| ICModelBasedEditor { |
| /** Marker used for synchronization from Problems View to the editor on double-click. */ |
| private IMarker fSyncProblemsViewMarker; |
| |
| /** |
| * A slightly modified implementation of IGotomarker compared to AbstractDecoratedTextEditor. |
| * |
| * @since 5.0 |
| */ |
| private final class GotoMarkerAdapter implements IGotoMarker { |
| @Override |
| public void gotoMarker(IMarker marker) { |
| if (fIsUpdatingMarkerViews) |
| return; |
| |
| if (getSourceViewer() == null) |
| return; |
| |
| int start= MarkerUtilities.getCharStart(marker); |
| int end= MarkerUtilities.getCharEnd(marker); |
| |
| boolean selectLine= start < 0 || end < 0; |
| |
| // Look up the current range of the marker when the document has been edited. |
| IAnnotationModel model= getDocumentProvider().getAnnotationModel(getEditorInput()); |
| if (model instanceof AbstractMarkerAnnotationModel) { |
| AbstractMarkerAnnotationModel markerModel= (AbstractMarkerAnnotationModel) model; |
| Position pos= markerModel.getMarkerPosition(marker); |
| if (pos != null && !pos.isDeleted()) { |
| // Use position instead of marker values |
| start= pos.getOffset(); |
| end= pos.getOffset() + pos.getLength(); |
| // Use position as is |
| selectLine= false; |
| } |
| |
| if (pos != null && pos.isDeleted()) { |
| // Do nothing if position has been deleted |
| return; |
| } |
| } |
| |
| IDocument document= getDocumentProvider().getDocument(getEditorInput()); |
| |
| if (selectLine) { |
| int line; |
| try { |
| if (start >= 0) { |
| IRegion lineInfo= document.getLineInformationOfOffset(start); |
| start= lineInfo.getOffset(); |
| end= start + lineInfo.getLength(); |
| } else { |
| line= MarkerUtilities.getLineNumber(marker); |
| // Marker line numbers are 1-based |
| -- line; |
| IRegion lineInfo= document.getLineInformation(line); |
| start= lineInfo.getOffset(); |
| end= start + lineInfo.getLength(); |
| } |
| } catch (BadLocationException e) { |
| return; |
| } |
| } |
| |
| int length= document.getLength(); |
| if (end - 1 < length && start < length) { |
| fSyncProblemsViewMarker = marker; |
| selectAndReveal(start, end - start); |
| } |
| } |
| } |
| |
| class AdaptedSourceViewer extends CSourceViewer { |
| |
| public AdaptedSourceViewer(Composite parent, IVerticalRuler verticalRuler, IOverviewRuler overviewRuler, |
| boolean showAnnotationsOverview, int styles, IPreferenceStore store) { |
| super(parent, verticalRuler, overviewRuler, showAnnotationsOverview, styles, store); |
| } |
| |
| @Override |
| public IContentAssistant getContentAssistant() { |
| return fContentAssistant; |
| } |
| |
| @Override |
| public void doOperation(int operation) { |
| if (getTextWidget() == null) |
| return; |
| |
| switch (operation) { |
| case CONTENTASSIST_PROPOSALS: |
| String msg= fContentAssistant.showPossibleCompletions(); |
| setStatusLineErrorMessage(msg); |
| return; |
| case QUICK_ASSIST: |
| /* |
| * TODO: We can get rid of this once the SourceViewer has a way to update |
| * the status line https://bugs.eclipse.org/bugs/show_bug.cgi?id=133787 |
| */ |
| msg= fQuickAssistAssistant.showPossibleQuickAssists(); |
| setStatusLineErrorMessage(msg); |
| return; |
| } |
| |
| super.doOperation(operation); |
| } |
| |
| @Override |
| public boolean requestWidgetToken(IWidgetTokenKeeper requester) { |
| if (PlatformUI.getWorkbench().getHelpSystem().isContextHelpDisplayed()) |
| return false; |
| return super.requestWidgetToken(requester); |
| } |
| |
| @Override |
| public boolean requestWidgetToken(IWidgetTokenKeeper requester, int priority) { |
| if (PlatformUI.getWorkbench().getHelpSystem().isContextHelpDisplayed()) |
| return false; |
| return super.requestWidgetToken(requester, priority); |
| } |
| |
| // This method is called only when the Platform version is below 4.5. |
| // TODO(sprigogin): Remove this override once compatibility with Platform 4.4 is no longer |
| // required. |
| @Override |
| protected IFormattingContext createFormattingContext() { |
| Point selectedRange = getSelectedRange(); |
| return createFormattingContext(selectedRange.x, selectedRange.y, false); |
| } |
| |
| @Override |
| protected IFormattingContext createFormattingContext(int selectionOffset, int selectionLength) { |
| return createFormattingContext(selectionOffset, selectionLength, true); |
| } |
| |
| private IFormattingContext createFormattingContext(int selectionOffset, int selectionLength, |
| boolean formattingScopeForEmptySelectionSupported) { |
| IFormattingContext context= new FormattingContext(); |
| |
| Map<String, Object> preferences; |
| ICElement inputCElement= getInputCElement(); |
| ICProject cProject= inputCElement != null ? inputCElement.getCProject() : null; |
| if (cProject == null) { |
| preferences= new HashMap<String, Object>(CCorePlugin.getOptions()); |
| } else { |
| preferences= new HashMap<String, Object>(cProject.getOptions(true)); |
| } |
| |
| if (inputCElement instanceof ITranslationUnit) { |
| ITranslationUnit tu= (ITranslationUnit) inputCElement; |
| ILanguage language; |
| try { |
| language= tu.getLanguage(); |
| } catch (CoreException e) { |
| // Use fallback CPP |
| language= GPPLanguage.getDefault(); |
| } |
| preferences.put(DefaultCodeFormatterConstants.FORMATTER_TRANSLATION_UNIT, tu); |
| preferences.put(DefaultCodeFormatterConstants.FORMATTER_LANGUAGE, language); |
| preferences.put(DefaultCodeFormatterConstants.FORMATTER_CURRENT_FILE, tu.getResource()); |
| boolean formatWholeDocument = false; |
| if (formattingScopeForEmptySelectionSupported && selectionLength == 0) { |
| // The selection is empty. Determine how it should be interpreted. |
| IPreferencesService preferenceService = Platform.getPreferencesService(); |
| boolean showDialog = preferenceService.getBoolean(CUIPlugin.PLUGIN_ID, |
| PreferenceConstants.FORMATTING_CONFIRM_SCOPE_FOR_EMPTY_SELECTION, true, null); |
| if (showDialog) { |
| if (!confirmFormattingScope()) { |
| // The user clicked Cancel. Abort the formatting operation. |
| context.dispose(); |
| return null; |
| } |
| } |
| String scope = preferenceService.getString(CUIPlugin.PLUGIN_ID, |
| PreferenceConstants.FORMATTING_SCOPE_FOR_EMPTY_SELECTION, |
| PreferenceConstants.FORMATTING_SCOPE_DOCUMENT, null); |
| if (PreferenceConstants.FORMATTING_SCOPE_DOCUMENT.equals(scope)) { |
| formatWholeDocument = true; |
| } else { |
| preferences.put(DefaultCodeFormatterConstants.FORMATTER_STATEMENT_SCOPE, Boolean.TRUE); |
| } |
| } |
| if (!formatWholeDocument) { |
| context.setProperty(FormattingContextProperties.CONTEXT_REGION, |
| new Region(selectionOffset, selectionLength)); |
| } |
| } |
| |
| if (cProject == null) { |
| // custom formatter specified? |
| String customFormatterId= getPreferenceStore().getString(CCorePreferenceConstants.CODE_FORMATTER); |
| if (customFormatterId != null) { |
| preferences.put(CCorePreferenceConstants.CODE_FORMATTER, customFormatterId); |
| } |
| } |
| context.setProperty(FormattingContextProperties.CONTEXT_PREFERENCES, preferences); |
| |
| return context; |
| } |
| |
| private boolean confirmFormattingScope() { |
| int redrawCount = 0; |
| while (!redraws()) { |
| redrawCount++; |
| setRedraw(true); |
| } |
| try { |
| Dialog dialog = new FormattingScopeDialog(getSite().getShell()); |
| return dialog.open() == Window.OK; |
| } finally { |
| while (--redrawCount >= 0) { |
| setRedraw(false); |
| } |
| } |
| } |
| } |
| |
| private static class ExitPolicy implements IExitPolicy { |
| final char fExitCharacter; |
| final char fEscapeCharacter; |
| final Deque<BracketLevel> fStack; |
| final int fSize; |
| ISourceViewer sourceViewer; |
| |
| public ExitPolicy(char exitCharacter, char escapeCharacter, Deque<BracketLevel> stack, ISourceViewer sViewer) { |
| fExitCharacter = exitCharacter; |
| fEscapeCharacter = escapeCharacter; |
| fStack = stack; |
| fSize = fStack.size(); |
| sourceViewer = sViewer; |
| } |
| |
| @Override |
| public ExitFlags doExit(LinkedModeModel model, VerifyEvent event, int offset, int length) { |
| if (fSize == fStack.size() && !isMasked(offset)) { |
| if (event.character == fExitCharacter) { |
| BracketLevel level = fStack.peek(); |
| if (level.fFirstPosition.offset > offset || level.fSecondPosition.offset < offset) |
| return null; |
| if (level.fSecondPosition.offset == offset && length == 0) |
| // don't enter the character if if its the closing peer |
| return new ExitFlags(ILinkedModeListener.UPDATE_CARET, false); |
| } |
| // When entering an anonymous class between the parenthesis', we don't want |
| // to jump after the closing parenthesis when return is pressed. |
| if (event.character == SWT.CR && offset > 0) { |
| IDocument document = sourceViewer.getDocument(); |
| try { |
| if (document.getChar(offset - 1) == '{') |
| return new ExitFlags(ILinkedModeListener.EXIT_ALL, true); |
| } catch (BadLocationException e) { |
| } |
| } |
| } |
| return null; |
| } |
| |
| private boolean isMasked(int offset) { |
| IDocument document = sourceViewer.getDocument(); |
| try { |
| return fEscapeCharacter == document.getChar(offset - 1); |
| } catch (BadLocationException e) { |
| } |
| return false; |
| } |
| } |
| |
| private static class BracketLevel { |
| LinkedModeUI fUI; |
| Position fFirstPosition; |
| Position fSecondPosition; |
| } |
| |
| /** |
| * Position updater that takes any changes at the borders of a position to not belong to the position. |
| * |
| * @since 4.0 |
| */ |
| private static class ExclusivePositionUpdater implements IPositionUpdater { |
| /** The position category. */ |
| private final String fCategory; |
| |
| /** |
| * Creates a new updater for the given {@code category}. |
| * |
| * @param category the new category. |
| */ |
| public ExclusivePositionUpdater(String category) { |
| fCategory = category; |
| } |
| |
| @Override |
| public void update(DocumentEvent event) { |
| int eventOffset = event.getOffset(); |
| int eventOldLength = event.getLength(); |
| int eventNewLength = event.getText() == null ? 0 : event.getText().length(); |
| int deltaLength = eventNewLength - eventOldLength; |
| |
| try { |
| Position[] positions = event.getDocument().getPositions(fCategory); |
| |
| for (int i = 0; i != positions.length; i++) { |
| |
| Position position = positions[i]; |
| |
| if (position.isDeleted()) |
| continue; |
| |
| int offset = position.getOffset(); |
| int length = position.getLength(); |
| int end = offset + length; |
| |
| if (offset >= eventOffset + eventOldLength) { |
| // position comes |
| // after change - shift |
| position.setOffset(offset + deltaLength); |
| } else if (end <= eventOffset) { |
| // position comes way before change - |
| // leave alone |
| } else if (offset <= eventOffset && end >= eventOffset + eventOldLength) { |
| // event completely internal to the position - adjust length |
| position.setLength(length + deltaLength); |
| } else if (offset < eventOffset) { |
| // event extends over end of position - adjust length |
| int newEnd = eventOffset; |
| position.setLength(newEnd - offset); |
| } else if (end > eventOffset + eventOldLength) { |
| // event extends from before position into it - adjust offset |
| // and length |
| // offset becomes end of event, length adjusted accordingly |
| int newOffset = eventOffset + eventNewLength; |
| position.setOffset(newOffset); |
| position.setLength(end - newOffset); |
| } else { |
| // event consumes the position - delete it |
| position.delete(); |
| } |
| } |
| } catch (BadPositionCategoryException e) { |
| // ignore and return |
| } |
| } |
| |
| // /** |
| // * Returns the position category. |
| // * |
| // * @return the position category |
| // */ |
| // public String getCategory() { |
| // return fCategory; |
| // } |
| } |
| |
| public static class BracketInserter implements VerifyKeyListener, ILinkedModeListener { |
| private boolean fCloseBrackets = true; |
| private boolean fCloseStrings = true; |
| private boolean fCloseAngularBrackets = true; |
| private boolean fCloseBraces = true; |
| private final String CATEGORY = toString(); |
| private final IPositionUpdater fUpdater = new ExclusivePositionUpdater(CATEGORY); |
| private final Deque<BracketLevel> fBracketLevelStack = new ArrayDeque<>(); |
| private ISourceViewer sourceViewer; |
| private boolean isGenericEditor; |
| private TextEditor fEditor; |
| |
| public BracketInserter(TextEditor editor, boolean isGenericEditor) { |
| fEditor = editor; |
| this.isGenericEditor = isGenericEditor; |
| } |
| |
| public void setSourceViewer(ISourceViewer sViewer) { |
| sourceViewer = sViewer; |
| } |
| |
| public void setCloseBracketsEnabled(boolean enabled) { |
| fCloseBrackets = enabled; |
| } |
| |
| public void setCloseStringsEnabled(boolean enabled) { |
| fCloseStrings = enabled; |
| } |
| |
| public void setCloseAngularBracketsEnabled(boolean enabled) { |
| fCloseAngularBrackets = enabled; |
| } |
| |
| public void setCloseBracesEnabled(boolean enabled) { |
| fCloseBraces = enabled; |
| } |
| |
| private boolean isAngularIntroducer(String identifier) { |
| return identifier.length() > 0 && (Character.isUpperCase(identifier.charAt(0)) |
| || angularIntroducers.contains(identifier) |
| || identifier.endsWith("_ptr") //$NON-NLS-1$ |
| || identifier.endsWith("_cast")); //$NON-NLS-1$ |
| } |
| |
| @Override |
| public void verifyKey(VerifyEvent event) { |
| // Early pruning to minimize overhead for normal typing. |
| if (!event.doit) |
| return; |
| |
| // Need to check that it is Generic Editor or CEditor before checking "Smart Insert" mode |
| // because Generic Editor doesn't have a "Smart Insert" mode. |
| if(!isGenericEditor) |
| if (fEditor.getInsertMode() != SMART_INSERT) |
| return; |
| switch (event.character) { |
| case '(': |
| case '<': |
| case '[': |
| case '{': |
| case '\'': |
| case '\"': |
| break; |
| default: |
| return; |
| } |
| |
| IDocument document = sourceViewer.getDocument(); |
| |
| final Point selection = sourceViewer.getSelectedRange(); |
| final int offset = selection.x; |
| final int length = selection.y; |
| try { |
| IRegion startLine = document.getLineInformationOfOffset(offset); |
| IRegion endLine = document.getLineInformationOfOffset(offset + length); |
| if (startLine != endLine && fEditor.isBlockSelectionModeEnabled()) { |
| return; |
| } |
| |
| ITypedRegion partition = TextUtilities.getPartition(document, ICPartitions.C_PARTITIONING, offset, true); |
| if (!IDocument.DEFAULT_CONTENT_TYPE.equals(partition.getType()) |
| && !ICPartitions.C_PREPROCESSOR.equals(partition.getType())) { |
| return; |
| } |
| |
| CHeuristicScanner scanner = new CHeuristicScanner(document, ICPartitions.C_PARTITIONING, partition.getType()); |
| int nextToken = scanner.nextToken(offset + length, endLine.getOffset() + endLine.getLength()); |
| String next = nextToken == Symbols.TokenEOF ? null : document.get(offset, scanner.getPosition() - offset).trim(); |
| int prevToken = scanner.previousToken(offset - 1, startLine.getOffset()); |
| int prevTokenOffset = scanner.getPosition() + 1; |
| String previous = prevToken == Symbols.TokenEOF ? null : document.get(prevTokenOffset, offset - prevTokenOffset).trim(); |
| |
| switch (event.character) { |
| case '(': |
| if (!fCloseBrackets |
| || nextToken == Symbols.TokenLPAREN |
| || nextToken == Symbols.TokenIDENT |
| || next != null && next.length() > 1) { |
| return; |
| } |
| break; |
| |
| case '<': |
| if (!(fCloseAngularBrackets && fCloseBrackets) |
| || nextToken == Symbols.TokenLESSTHAN |
| || prevToken != Symbols.TokenIDENT |
| || !isAngularIntroducer(previous)) { |
| return; |
| } |
| break; |
| |
| case '[': |
| if (!fCloseBrackets |
| || nextToken == Symbols.TokenIDENT |
| || next != null && next.length() > 1) { |
| return; |
| } |
| break; |
| |
| case '{': |
| // An opening brace inside parentheses probably starts an initializer list - |
| // close it. |
| if (!fCloseBraces |
| || nextToken == Symbols.TokenIDENT |
| || next != null && next.length() > 1 |
| || !isInsideParentheses(scanner, offset - 1)) { |
| return; |
| } |
| break; |
| |
| case '\'': |
| case '"': |
| if (!fCloseStrings |
| || nextToken == Symbols.TokenIDENT |
| || next != null && (next.length() > 1 || next.charAt(0) == event.character) |
| || isInsideStringInPreprocessorDirective(partition, document, offset)) { |
| return; |
| } |
| break; |
| |
| default: |
| return; |
| } |
| |
| if (!fEditor.validateEditorInputState()) |
| return; |
| |
| final char character = event.character; |
| final char closingCharacter = getPeerCharacter(character); |
| final StringBuilder buffer = new StringBuilder(3); |
| buffer.append(character); |
| buffer.append(closingCharacter); |
| |
| document.replace(offset, length, buffer.toString()); |
| |
| BracketLevel level = new BracketLevel(); |
| fBracketLevelStack.push(level); |
| |
| LinkedPositionGroup group = new LinkedPositionGroup(); |
| group.addPosition(new LinkedPosition(document, offset + 1, 0, LinkedPositionGroup.NO_STOP)); |
| |
| LinkedModeModel model = new LinkedModeModel(); |
| model.addLinkingListener(this); |
| model.addGroup(group); |
| model.forceInstall(); |
| |
| // Set up position tracking for our magic peers. |
| if (fBracketLevelStack.size() == 1) { |
| document.addPositionCategory(CATEGORY); |
| document.addPositionUpdater(fUpdater); |
| } |
| level.fFirstPosition = new Position(offset, 1); |
| level.fSecondPosition = new Position(offset + 1, 1); |
| document.addPosition(CATEGORY, level.fFirstPosition); |
| document.addPosition(CATEGORY, level.fSecondPosition); |
| |
| level.fUI = new EditorLinkedModeUI(model, sourceViewer); |
| level.fUI.setSimpleMode(true); |
| level.fUI.setExitPolicy(new ExitPolicy(closingCharacter, getEscapeCharacter(closingCharacter), fBracketLevelStack, sourceViewer)); |
| level.fUI.setExitPosition(sourceViewer, offset + 2, 0, Integer.MAX_VALUE); |
| level.fUI.setCyclingMode(LinkedModeUI.CYCLE_NEVER); |
| level.fUI.enter(); |
| |
| IRegion newSelection = level.fUI.getSelectedRegion(); |
| sourceViewer.setSelectedRange(newSelection.getOffset(), newSelection.getLength()); |
| |
| event.doit = false; |
| } catch (BadLocationException | BadPositionCategoryException e) { |
| CUIPlugin.log(e); |
| } |
| } |
| |
| private boolean isInsideParentheses(CHeuristicScanner scanner, int offset) { |
| int depth = 0; |
| // Limit the scanning distance to 100 tokens. |
| for (int i = 0; i < 100; i++) { |
| int token = scanner.previousToken(offset, 0); |
| if (token == Symbols.TokenLPAREN) { |
| if (--depth < 0) |
| return true; |
| } else if (token == Symbols.TokenRPAREN) { |
| ++depth; |
| } else if (token == Symbols.TokenEOF) { |
| return false; |
| } |
| offset = scanner.getPosition(); |
| } |
| return false; |
| } |
| |
| private boolean isInsideStringInPreprocessorDirective(ITypedRegion partition, IDocument document, int offset) throws BadLocationException { |
| if (ICPartitions.C_PREPROCESSOR.equals(partition.getType()) && offset < document.getLength()) { |
| // Use temporary document to test whether offset is inside non-default partition. |
| String directive = document.get(partition.getOffset(), offset - partition.getOffset() + 1); |
| int hashIdx = directive.indexOf('#'); |
| if (hashIdx >= 0) { |
| IDocument tmp = new Document(directive.substring(hashIdx + 1)); |
| new CDocumentSetupParticipant().setup(tmp); |
| String type = TextUtilities.getContentType(tmp, ICPartitions.C_PARTITIONING, offset - (partition.getOffset() + hashIdx + 1), true); |
| if (!type.equals(IDocument.DEFAULT_CONTENT_TYPE)) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public void left(LinkedModeModel environment, int flags) { |
| final BracketLevel level = fBracketLevelStack.pop(); |
| |
| if (flags != ILinkedModeListener.EXTERNAL_MODIFICATION) |
| return; |
| |
| // remove brackets |
| final IDocument document = sourceViewer.getDocument(); |
| if (document instanceof IDocumentExtension) { |
| IDocumentExtension extension = (IDocumentExtension) document; |
| extension.registerPostNotificationReplace(null, new IDocumentExtension.IReplace() { |
| @Override |
| public void perform(IDocument d, IDocumentListener owner) { |
| if ((level.fFirstPosition.isDeleted || level.fFirstPosition.length == 0) |
| && !level.fSecondPosition.isDeleted |
| && level.fSecondPosition.offset == level.fFirstPosition.offset) { |
| try { |
| document.replace(level.fSecondPosition.offset, |
| level.fSecondPosition.length, |
| null); |
| } catch (BadLocationException e) { |
| CUIPlugin.log(e); |
| } |
| } |
| |
| if (fBracketLevelStack.size() == 0) { |
| document.removePositionUpdater(fUpdater); |
| try { |
| document.removePositionCategory(CATEGORY); |
| } catch (BadPositionCategoryException e) { |
| CUIPlugin.log(e); |
| } |
| } |
| } |
| }); |
| } |
| } |
| |
| @Override |
| public void suspend(LinkedModeModel environment) { |
| } |
| |
| @Override |
| public void resume(LinkedModeModel environment, int flags) { |
| } |
| } |
| |
| /** |
| * Updates the C outline page selection and this editor's range indicator. |
| * |
| * @since 3.0 |
| */ |
| private class EditorSelectionChangedListener extends AbstractSelectionChangedListener { |
| @Override |
| public void selectionChanged(SelectionChangedEvent event) { |
| // TODO: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=56161 |
| CEditor.this.selectionChanged(); |
| } |
| } |
| |
| /** |
| * Text navigation action to navigate to the next sub-word. |
| * |
| * @since 4.0 |
| */ |
| protected abstract class NextSubWordAction extends TextNavigationAction { |
| protected CWordIterator fIterator = new CWordIterator(); |
| |
| /** |
| * 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 sub word navigation is enabled. |
| final IPreferenceStore store = getPreferenceStore(); |
| if (!store.getBoolean(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; |
| while (position != BreakIterator.DONE && widget == -1) { // TODO: optimize |
| position = fIterator.following(position); |
| if (position != BreakIterator.DONE) |
| widget = modelOffset2WidgetOffset(viewer, position); |
| } |
| return position; |
| } |
| |
| /** |
| * Sets the caret position to the sub-word boundary given with {@code position}. |
| * |
| * @param position Position where the action should move the caret |
| */ |
| protected abstract void setCaretPosition(int position); |
| } |
| |
| /** |
| * Text navigation action to navigate to the next sub-word. |
| * |
| * @since 4.0 |
| */ |
| 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 operation action to delete the next sub-word. |
| * |
| * @since 4.0 |
| */ |
| protected class DeleteNextSubWordAction extends NextSubWordAction implements IUpdate { |
| /** |
| * Creates a new delete next sub-word action. |
| */ |
| public DeleteNextSubWordAction() { |
| super(ST.DELETE_WORD_NEXT); |
| } |
| |
| @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 e) { |
| // Should not happen |
| } |
| } |
| } |
| |
| @Override |
| protected int findNextPosition(int position) { |
| return fIterator.following(position); |
| } |
| |
| @Override |
| public void update() { |
| setEnabled(isEditorInputModifiable()); |
| } |
| } |
| |
| /** |
| * Text operation action to select the next sub-word. |
| * |
| * @since 4.0 |
| */ |
| 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 navigation action to navigate to the previous sub-word. |
| * |
| * @since 4.0 |
| */ |
| protected abstract class PreviousSubWordAction extends TextNavigationAction { |
| protected CWordIterator fIterator = new CWordIterator(); |
| |
| /** |
| * 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 sub word navigation is enabled. |
| final IPreferenceStore store = getPreferenceStore(); |
| if (!store.getBoolean(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 e) { |
| // 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; |
| while (position != BreakIterator.DONE && widget == -1) { // TODO: optimize |
| position = fIterator.preceding(position); |
| if (position != BreakIterator.DONE) |
| widget = modelOffset2WidgetOffset(viewer, position); |
| } |
| return position; |
| } |
| |
| /** |
| * Sets the caret position to the sub-word boundary given with {@code position}. |
| * |
| * @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. |
| * |
| * @since 4.0 |
| */ |
| 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 operation action to delete the previous sub-word. |
| * |
| * @since 4.0 |
| */ |
| protected class DeletePreviousSubWordAction extends PreviousSubWordAction implements IUpdate { |
| /** |
| * Creates a new delete previous sub-word action. |
| */ |
| public DeletePreviousSubWordAction() { |
| super(ST.DELETE_WORD_PREVIOUS); |
| } |
| |
| @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 e) { |
| // Should not happen |
| } |
| } |
| } |
| |
| @Override |
| protected int findPreviousPosition(int position) { |
| return fIterator.preceding(position); |
| } |
| |
| @Override |
| public void update() { |
| setEnabled(isEditorInputModifiable()); |
| } |
| } |
| |
| /** |
| * Text operation action to select the previous sub-word. |
| * |
| * @since 4.0 |
| */ |
| 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); |
| } |
| } |
| } |
| } |
| |
| /** |
| * The editor selection changed listener. |
| * |
| * @since 3.0 |
| */ |
| private EditorSelectionChangedListener fEditorSelectionChangedListener; |
| |
| /** |
| * Time when last error message got set. |
| * |
| * @since 5.3 |
| */ |
| private long fErrorMessageTime; |
| |
| /** |
| * Timeout for the error message. |
| * |
| * @since 5.3 |
| */ |
| private static final long ERROR_MESSAGE_TIMEOUT= 1000; |
| |
| /** The outline page */ |
| protected CContentOutlinePage fOutlinePage; |
| |
| /** Search actions **/ |
| private ActionGroup fSelectionSearchGroup; |
| private ActionGroup fTextSearchGroup; |
| private CRefactoringActionGroup fRefactoringActionGroup; |
| private ActionGroup fOpenInViewGroup; |
| |
| /** Generate action group filling the "Source" submenu */ |
| private GenerateActionGroup fGenerateActionGroup; |
| |
| /** Generate action group filling the "Surround with" submenu */ |
| private SurroundWithActionGroup fSurroundWithActionGroup; |
| |
| /** Pairs of brackets, used to match. */ |
| protected static final char[] BRACKETS = { '{', '}', '(', ')', '[', ']', '<', '>' }; |
| |
| /** Matches the brackets. */ |
| protected CPairMatcher fBracketMatcher = new CPairMatcher(BRACKETS); |
| |
| /** The bracket inserter. */ |
| private final BracketInserter fBracketInserter = new BracketInserter(this, false); |
| |
| /** Listener to annotation model changes that updates the error tick in the tab image */ |
| private CEditorErrorTickUpdater fCEditorErrorTickUpdater; |
| |
| /** Preference key for sub-word navigation, aka smart caret positioning */ |
| public static final String SUB_WORD_NAVIGATION = "subWordNavigation"; //$NON-NLS-1$ |
| /** Preference key for matching brackets */ |
| public static final String MATCHING_BRACKETS = "matchingBrackets"; //$NON-NLS-1$ |
| /** Preference key for matching brackets color */ |
| public static final String MATCHING_BRACKETS_COLOR = "matchingBracketsColor"; //$NON-NLS-1$ |
| /** Preference key for inactive code painter enablement */ |
| public static final String INACTIVE_CODE_ENABLE = "inactiveCodeEnable"; //$NON-NLS-1$ |
| /** Preference key for inactive code painter color */ |
| public static final String INACTIVE_CODE_COLOR = "inactiveCodeColor"; //$NON-NLS-1$ |
| /** Preference key for automatically closing strings */ |
| private static final String CLOSE_STRINGS = PreferenceConstants.EDITOR_CLOSE_STRINGS; |
| /** Preference key for automatically closing brackets and parenthesis */ |
| private static final String CLOSE_BRACKETS = PreferenceConstants.EDITOR_CLOSE_BRACKETS; |
| /** Preference key for automatically closing angular brackets */ |
| private static final String CLOSE_ANGULAR_BRACKETS = PreferenceConstants.EDITOR_CLOSE_ANGULAR_BRACKETS; |
| /** Preference key for automatically closing curly braces */ |
| private static final String CLOSE_BRACES = PreferenceConstants.EDITOR_CLOSE_BRACES; |
| |
| /** Preference key for compiler task tags */ |
| private static final String TODO_TASK_TAGS = CCorePreferenceConstants.TODO_TASK_TAGS; |
| |
| /** |
| * This editor's projection support |
| */ |
| protected ProjectionSupport fProjectionSupport; |
| /** |
| * This editor's projection model updater |
| */ |
| private ICFoldingStructureProvider fProjectionModelUpdater; |
| |
| /** |
| * The action group for folding. |
| */ |
| private FoldingActionGroup fFoldingGroup; |
| |
| /** |
| * AST reconciling listeners. |
| * @since 4.0 |
| */ |
| private final ListenerList<ICReconcilingListener> fReconcilingListeners= |
| new ListenerList<ICReconcilingListener>(ListenerList.IDENTITY); |
| |
| /** |
| * Semantic highlighting manager |
| * @since 4.0 |
| */ |
| private SemanticHighlightingManager fSemanticManager; |
| |
| /** |
| * True if editor is opening a large file. |
| * @since 5.0 |
| */ |
| private boolean fEnableScalablilityMode; |
| |
| /** Flag indicating whether the reconciler is currently running. */ |
| private volatile boolean fIsReconciling; |
| |
| private CTemplatesPage fTemplatesPage; |
| |
| private SelectionHistory fSelectionHistory; |
| |
| private final IndexUpdateRequestor fIndexUpdateRequestor = new IndexUpdateRequestor(); |
| |
| private final ListenerList<IPostSaveListener> fPostSaveListeners; |
| |
| private static final Set<String> angularIntroducers = new HashSet<>(); |
| static { |
| angularIntroducers.add("template"); //$NON-NLS-1$ |
| angularIntroducers.add("vector"); //$NON-NLS-1$ |
| angularIntroducers.add("deque"); //$NON-NLS-1$ |
| angularIntroducers.add("list"); //$NON-NLS-1$ |
| angularIntroducers.add("slist"); //$NON-NLS-1$ |
| angularIntroducers.add("map"); //$NON-NLS-1$ |
| angularIntroducers.add("set"); //$NON-NLS-1$ |
| angularIntroducers.add("multimap"); //$NON-NLS-1$ |
| angularIntroducers.add("multiset"); //$NON-NLS-1$ |
| angularIntroducers.add("hash_map"); //$NON-NLS-1$ |
| angularIntroducers.add("hash_set"); //$NON-NLS-1$ |
| angularIntroducers.add("hash_multimap"); //$NON-NLS-1$ |
| angularIntroducers.add("hash_multiset"); //$NON-NLS-1$ |
| angularIntroducers.add("unordered_map"); //$NON-NLS-1$ |
| angularIntroducers.add("unordered_set"); //$NON-NLS-1$ |
| angularIntroducers.add("unordered_multimap"); //$NON-NLS-1$ |
| angularIntroducers.add("unordered_multiset"); //$NON-NLS-1$ |
| angularIntroducers.add("pair"); //$NON-NLS-1$ |
| angularIntroducers.add("tuple"); //$NON-NLS-1$ |
| angularIntroducers.add("include"); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Default constructor. |
| */ |
| public CEditor() { |
| setDocumentProvider(CUIPlugin.getDefault().getDocumentProvider()); |
| |
| setEditorContextMenuId("#CEditorContext"); //$NON-NLS-1$ |
| setRulerContextMenuId("#CEditorRulerContext"); //$NON-NLS-1$ |
| setOutlinerContextMenuId("#CEditorOutlinerContext"); //$NON-NLS-1$ |
| |
| fCEditorErrorTickUpdater = new CEditorErrorTickUpdater(this); |
| fPostSaveListeners = new ListenerList<IPostSaveListener>(); |
| } |
| |
| @Override |
| protected void initializeEditor() { |
| } |
| |
| @Override |
| protected void doSetInput(IEditorInput input) throws CoreException { |
| ISourceViewer sourceViewer= getSourceViewer(); |
| if (!(sourceViewer instanceof ISourceViewerExtension2)) { |
| setPreferenceStore(createCombinedPreferenceStore(input)); |
| internalDoSetInput(input); |
| updateScalabilityMode(input); |
| return; |
| } |
| |
| getDocumentProvider().connect(input); |
| try { |
| // Uninstall & unregister preference store listener |
| getSourceViewerDecorationSupport(sourceViewer).uninstall(); |
| ((ISourceViewerExtension2) sourceViewer).unconfigure(); |
| |
| setPreferenceStore(createCombinedPreferenceStore(input)); |
| updateScalabilityMode(input); |
| |
| // Install & register preference store listener |
| sourceViewer.configure(getSourceViewerConfiguration()); |
| getSourceViewerDecorationSupport(sourceViewer).install(getPreferenceStore()); |
| |
| internalDoSetInput(input); |
| } finally { |
| getDocumentProvider().disconnect(input); |
| } |
| } |
| |
| private void internalDoSetInput(IEditorInput input) throws CoreException { |
| ISourceViewer sourceViewer= getSourceViewer(); |
| CSourceViewer cSourceViewer= null; |
| if (sourceViewer instanceof CSourceViewer) { |
| cSourceViewer= (CSourceViewer) sourceViewer; |
| } |
| |
| IPreferenceStore store= getPreferenceStore(); |
| if (cSourceViewer != null && isFoldingEnabled() && (store == null || !store.getBoolean(PreferenceConstants.EDITOR_SHOW_SEGMENTS))) |
| cSourceViewer.prepareDelayedProjection(); |
| |
| super.doSetInput(input); |
| |
| setOutlinePageInput(fOutlinePage, input); |
| |
| if (fProjectionModelUpdater != null) { |
| fProjectionModelUpdater.initialize(); |
| } |
| if (fCEditorErrorTickUpdater != null) { |
| fCEditorErrorTickUpdater.updateEditorImage(getInputCElement()); |
| } |
| ICElement element= getInputCElement(); |
| if (element instanceof ITranslationUnit) { |
| ITranslationUnit tu = (ITranslationUnit) element; |
| fIndexUpdateRequestor.updateIndexInclusion(tu); |
| fBracketMatcher.configure(tu.getLanguage()); |
| } else { |
| fIndexUpdateRequestor.updateIndexInclusion(null); |
| fBracketMatcher.configure(null); |
| } |
| } |
| |
| private void updateScalabilityMode(IEditorInput input) { |
| int lines = getDocumentProvider().getDocument(input).getNumberOfLines(); |
| boolean wasEnabled = fEnableScalablilityMode; |
| fEnableScalablilityMode = lines > getPreferenceStore().getInt(PreferenceConstants.SCALABILITY_NUMBER_OF_LINES); |
| if (fEnableScalablilityMode && !wasEnabled) { |
| // Alert users that scalability mode should be turned on |
| if (getPreferenceStore().getBoolean(PreferenceConstants.SCALABILITY_ALERT)) { |
| MessageDialogWithToggle dialog = new MessageDialogWithToggle( |
| getSite().getShell(), |
| CEditorMessages.Scalability_info, |
| null, |
| CEditorMessages.Scalability_message, |
| MessageDialog.INFORMATION, |
| new String[] {IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL}, 0, |
| CEditorMessages.Scalability_reappear, |
| false) { |
| { |
| setShellStyle(SWT.DIALOG_TRIM | SWT.MODELESS | SWT.ON_TOP | getDefaultOrientation()); |
| } |
| @Override |
| protected void buttonPressed(int buttonId) { |
| PreferenceConstants.getPreferenceStore().setValue(PreferenceConstants.SCALABILITY_ALERT, !getToggleState()); |
| super.buttonPressed(buttonId); |
| if (buttonId == IDialogConstants.YES_ID) { |
| PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(getSite().getShell(), |
| "org.eclipse.cdt.ui.preferences.CScalabilityPreferences", null, null); //$NON-NLS-1$ |
| dialog.open(); |
| } |
| } |
| }; |
| dialog.setBlockOnOpen(false); |
| dialog.open(); |
| } |
| } |
| } |
| |
| @Override |
| protected void setPreferenceStore(IPreferenceStore store) { |
| super.setPreferenceStore(store); |
| SourceViewerConfiguration sourceViewerConfiguration= getSourceViewerConfiguration(); |
| if (!(sourceViewerConfiguration instanceof CSourceViewerConfiguration)) { |
| CTextTools textTools= CUIPlugin.getDefault().getTextTools(); |
| setSourceViewerConfiguration(new CSourceViewerScalableConfiguration(textTools.getColorManager(), store, this, ICPartitions.C_PARTITIONING)); |
| } |
| |
| if (getSourceViewer() instanceof CSourceViewer) |
| ((CSourceViewer) getSourceViewer()).setPreferenceStore(store); |
| |
| fMarkOccurrenceAnnotations= store.getBoolean(PreferenceConstants.EDITOR_MARK_OCCURRENCES); |
| fMarkOverloadedOperatorOccurrences= store.getBoolean(PreferenceConstants.EDITOR_MARK_OVERLOADED_OPERATOR_OCCURRENCES); |
| fStickyOccurrenceAnnotations= store.getBoolean(PreferenceConstants.EDITOR_STICKY_OCCURRENCES); |
| } |
| |
| /** |
| * Update the title image. |
| * @param image Title image. |
| */ |
| public void updatedTitleImage(Image image) { |
| setTitleImage(image); |
| } |
| |
| /** |
| * Returns the working copy wrapped by this editors input. |
| * |
| * @return the working copy wrapped by this editors input. |
| * @since 3.0 |
| */ |
| public IWorkingCopy getInputCElement () { |
| return CUIPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(getEditorInput()); |
| } |
| |
| @Override |
| public ITranslationUnit getTranslationUnit() { |
| return getInputCElement(); |
| } |
| |
| /** |
| * @see org.eclipse.ui.ISaveablePart#isSaveAsAllowed() |
| */ |
| @Override |
| public boolean isSaveAsAllowed() { |
| return true; |
| } |
| |
| /** |
| * Returns the outline page of the C/C++ editor. |
| * @return Outline page. |
| */ |
| public CContentOutlinePage getOutlinePage() { |
| if (fOutlinePage == null) { |
| fOutlinePage = new CContentOutlinePage(this); |
| fOutlinePage.addSelectionChangedListener(this); |
| } |
| setOutlinePageInputIfNotSame(fOutlinePage, getEditorInput()); |
| return fOutlinePage; |
| } |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public <T> T getAdapter(Class<T> adapterClass) { |
| if (adapterClass.isAssignableFrom(IContentOutlinePage.class)) { |
| return (T) getOutlinePage(); |
| } else if (adapterClass.isAssignableFrom(IShowInTargetList.class)) { |
| return (T) new IShowInTargetList() { |
| @Override |
| @SuppressWarnings("deprecation") |
| public String[] getShowInTargetIds() { |
| return new String[] { IPageLayout.ID_PROJECT_EXPLORER, IPageLayout.ID_OUTLINE, IPageLayout.ID_RES_NAV }; |
| } |
| }; |
| } else if (adapterClass.isAssignableFrom(IShowInSource.class)) { |
| ICElement ce= getElementAt(getSourceViewer().getSelectedRange().x, false); |
| if (ce instanceof ITranslationUnit) { |
| ce = null; |
| } |
| final ISelection selection= ce != null ? new StructuredSelection(ce) : null; |
| return (T) new IShowInSource() { |
| @Override |
| public ShowInContext getShowInContext() { |
| return new ShowInContext(getEditorInput(), selection); |
| } |
| }; |
| } else if (adapterClass.isAssignableFrom(ProjectionAnnotationModel.class)) { |
| if (fProjectionSupport != null) { |
| T adapter = fProjectionSupport.getAdapter(getSourceViewer(), adapterClass); |
| if (adapter != null) |
| return adapter; |
| } |
| } else if (adapterClass.isAssignableFrom(IContextProvider.class)) { |
| return (T) new CUIHelp.CUIHelpContextProvider(this); |
| } else if (adapterClass.isAssignableFrom(IGotoMarker.class)) { |
| return (T) new GotoMarkerAdapter(); |
| } else if (adapterClass.isAssignableFrom(ITemplatesPage.class)) { |
| if (fTemplatesPage == null) { |
| fTemplatesPage = new CTemplatesPage(this); |
| } |
| return (T) fTemplatesPage; |
| } else if (adapterClass.isAssignableFrom(ITranslationUnitHolder.class)) |
| return (T) this; |
| return super.getAdapter(adapterClass); |
| } |
| |
| /** |
| * Handles a property change event describing a change |
| * of the editor's preference store and updates the preference |
| * related editor properties. |
| * |
| * @param event the property change event |
| */ |
| @Override |
| protected void handlePreferenceStoreChanged(PropertyChangeEvent event) { |
| String property = event.getProperty(); |
| |
| if (AbstractDecoratedTextEditorPreferenceConstants.EDITOR_TAB_WIDTH.equals(property)) { |
| /* |
| * Ignore tab setting since we rely on the formatter preferences. |
| * We do this outside the try-finally block to avoid that EDITOR_TAB_WIDTH |
| * is handled by the base-class (AbstractDecoratedTextEditor). |
| */ |
| return; |
| } |
| if (AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SPACES_FOR_TABS.equals(property)) { |
| // Ignore spaces-for-tab setting since we rely on the formatter preferences. |
| return; |
| } |
| |
| try { |
| final AdaptedSourceViewer asv = (AdaptedSourceViewer) getSourceViewer(); |
| |
| if (asv != null) { |
| boolean newBooleanValue= false; |
| Object newValue= event.getNewValue(); |
| if (newValue != null) |
| newBooleanValue= Boolean.valueOf(newValue.toString()).booleanValue(); |
| |
| if (CLOSE_BRACKETS.equals(property)) { |
| fBracketInserter.setCloseBracketsEnabled(newBooleanValue); |
| return; |
| } |
| |
| if (CLOSE_ANGULAR_BRACKETS.equals(property)) { |
| fBracketInserter.setCloseAngularBracketsEnabled(newBooleanValue); |
| return; |
| } |
| |
| if (CLOSE_BRACES.equals(property)) { |
| fBracketInserter.setCloseBracesEnabled(newBooleanValue); |
| return; |
| } |
| |
| if (CLOSE_STRINGS.equals(property)) { |
| fBracketInserter.setCloseStringsEnabled(newBooleanValue); |
| return; |
| } |
| |
| if (PreferenceConstants.EDITOR_TEXT_HOVER_MODIFIERS.equals(property)) |
| updateHoverBehavior(); |
| |
| ((CSourceViewerConfiguration) getSourceViewerConfiguration()).handlePropertyChangeEvent(event); |
| |
| if (PreferenceConstants.EDITOR_SMART_TAB.equals(property)) { |
| if (newBooleanValue) { |
| setActionActivationCode("IndentOnTab", '\t', -1, SWT.NONE); //$NON-NLS-1$ |
| } else { |
| removeActionActivationCode("IndentOnTab"); //$NON-NLS-1$ |
| } |
| return; |
| } |
| |
| if (TODO_TASK_TAGS.equals(event.getProperty())) { |
| ISourceViewer sourceViewer = getSourceViewer(); |
| if (sourceViewer != null && affectsTextPresentation(event)) |
| sourceViewer.invalidateTextPresentation(); |
| return; |
| } |
| |
| if (affectsOverrideIndicatorAnnotations(event)) { |
| if (isShowingOverrideIndicators()) { |
| if (fOverrideIndicatorManager == null) |
| installOverrideIndicator(true); |
| } else { |
| if (fOverrideIndicatorManager != null) |
| uninstallOverrideIndicator(); |
| } |
| return; |
| } |
| |
| if (PreferenceConstants.EDITOR_FOLDING_PROVIDER.equals(property)) { |
| if (fProjectionModelUpdater != null) { |
| fProjectionModelUpdater.uninstall(); |
| } |
| // either freshly enabled or provider changed |
| fProjectionModelUpdater = CUIPlugin.getDefault().getFoldingStructureProviderRegistry().getCurrentFoldingProvider(); |
| if (fProjectionModelUpdater != null) { |
| fProjectionModelUpdater.install(this, asv); |
| } |
| return; |
| } |
| |
| if (DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE.equals(property) |
| || DefaultCodeFormatterConstants.FORMATTER_INDENTATION_SIZE.equals(property) |
| || DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR.equals(property)) { |
| StyledText textWidget= asv.getTextWidget(); |
| int tabWidth= getSourceViewerConfiguration().getTabWidth(asv); |
| if (textWidget.getTabs() != tabWidth) |
| textWidget.setTabs(tabWidth); |
| uninstallTabsToSpacesConverter(); |
| if (isTabsToSpacesConversionEnabled()) { |
| installTabsToSpacesConverter(); |
| } else { |
| updateIndentationMode(); |
| } |
| return; |
| } |
| |
| if (PreferenceConstants.EDITOR_MARK_OCCURRENCES.equals(property)) { |
| if (newBooleanValue != fMarkOccurrenceAnnotations) { |
| fMarkOccurrenceAnnotations= newBooleanValue; |
| if (!fMarkOccurrenceAnnotations) |
| uninstallOccurrencesFinder(); |
| else |
| installOccurrencesFinder(true); |
| } |
| return; |
| } |
| if (PreferenceConstants.EDITOR_STICKY_OCCURRENCES.equals(property)) { |
| fStickyOccurrenceAnnotations= newBooleanValue; |
| return; |
| } |
| if (PreferenceConstants.EDITOR_MARK_OVERLOADED_OPERATOR_OCCURRENCES.equals(property)) { |
| fMarkOverloadedOperatorOccurrences= newBooleanValue; |
| return; |
| } |
| |
| if (SemanticHighlightings.affectsEnablement(getPreferenceStore(), event) |
| || (isEnableScalablilityMode() && PreferenceConstants.SCALABILITY_SEMANTIC_HIGHLIGHT.equals(property))) { |
| if (isSemanticHighlightingEnabled()) { |
| installSemanticHighlighting(); |
| fSemanticManager.refresh(); |
| } else { |
| uninstallSemanticHighlighting(); |
| } |
| return; |
| } |
| |
| // For Scalability |
| if (isEnableScalablilityMode()) { |
| if (PreferenceConstants.SCALABILITY_RECONCILER.equals(property) || |
| PreferenceConstants.SCALABILITY_SYNTAX_COLOR.equals(property)) { |
| BusyIndicator.showWhile(getSite().getShell().getDisplay(), new Runnable() { |
| @Override |
| public void run() { |
| setOutlinePageInput(fOutlinePage, getEditorInput()); |
| asv.unconfigure(); |
| asv.configure(getSourceViewerConfiguration()); |
| }}); |
| return; |
| } |
| } |
| |
| IContentAssistant c = asv.getContentAssistant(); |
| if (c instanceof ContentAssistant) { |
| ContentAssistPreference.changeConfiguration((ContentAssistant) c, getPreferenceStore(), event); |
| } |
| } |
| } finally { |
| super.handlePreferenceStoreChanged(event); |
| } |
| } |
| |
| @Override |
| protected void initializeViewerColors(ISourceViewer viewer) { |
| // is handled by CSourceViewer |
| } |
| |
| /** |
| * Updates the hovering behavior depending on the preferences. |
| */ |
| private void updateHoverBehavior() { |
| SourceViewerConfiguration configuration= getSourceViewerConfiguration(); |
| String[] types= configuration.getConfiguredContentTypes(getSourceViewer()); |
| |
| for (String t : types) { |
| |
| 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 stateMask : stateMasks) { |
| 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); |
| } |
| } |
| } |
| |
| /** |
| * Reacts to changed selection in the editor. |
| * |
| * @since 3.0 |
| */ |
| protected void selectionChanged() { |
| if (getSelectionProvider() == null) |
| return; |
| ISourceReference element= computeHighlightRangeSourceReference(); |
| updateStatusLine(); |
| synchronizeOutlinePage(); |
| setSelection(element, false); |
| } |
| |
| /** |
| * Computes and returns the source reference that includes the caret and |
| * serves as provider for the outline page selection and the editor range |
| * indication. |
| * |
| * @return the computed source reference |
| * @since 4.0 |
| */ |
| protected ISourceReference computeHighlightRangeSourceReference() { |
| ISourceViewer sourceViewer= getSourceViewer(); |
| if (sourceViewer == null) |
| return null; |
| |
| StyledText styledText= sourceViewer.getTextWidget(); |
| if (styledText == null) |
| return null; |
| |
| int caret= 0; |
| if (sourceViewer instanceof ITextViewerExtension5) { |
| ITextViewerExtension5 extension= (ITextViewerExtension5) sourceViewer; |
| caret= extension.widgetOffset2ModelOffset(styledText.getSelection().x); |
| } else { |
| int offset= sourceViewer.getVisibleRegion().getOffset(); |
| caret= offset + styledText.getSelection().x; |
| } |
| |
| ICElement element= getElementAt(caret, false); |
| |
| if (!(element instanceof ISourceReference)) |
| return null; |
| |
| return (ISourceReference) element; |
| } |
| |
| /** |
| * Returns the most narrow element including the given offset. If {@code reconcile} |
| * is {@code true} the editor's input element is reconciled in advance. If it is |
| * {@code false} this method only returns a result if the editor's input element |
| * does not need to be reconciled. |
| * |
| * @param offset the offset included by the retrieved element |
| * @param reconcile {@code true} if working copy should be reconciled |
| * @return the most narrow element which includes the given offset |
| */ |
| protected ICElement getElementAt(int offset, boolean reconcile) { |
| IWorkingCopy unit= getInputCElement(); |
| |
| if (unit != null) { |
| try { |
| if (reconcile) { |
| synchronized (unit) { |
| unit.reconcile(); |
| } |
| return unit.getElementAtOffset(offset); |
| } else if (unit.isStructureKnown() && unit.isConsistent() && !fIsReconciling) { |
| return unit.getElementAtOffset(offset); |
| } |
| } catch (CModelException e) { |
| CUIPlugin.log(e.getStatus()); |
| // Nothing found, be tolerant and go on. |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Synchronizes the outline view selection with the given element position in the editor. |
| * |
| * @since 4.0 |
| */ |
| protected void synchronizeOutlinePage() { |
| if (fOutlinePage != null && fOutlinePage.isLinkingEnabled()) { |
| fOutlinePage.removeSelectionChangedListener(this); |
| fOutlinePage.synchronizeSelectionWithEditor(); |
| fOutlinePage.addSelectionChangedListener(this); |
| } |
| } |
| |
| /** |
| * React to changed selection in the outline view. |
| */ |
| @Override |
| public void selectionChanged(SelectionChangedEvent event) { |
| ISelection sel = event.getSelection(); |
| if (sel instanceof IStructuredSelection) { |
| IStructuredSelection selection = (IStructuredSelection) sel; |
| Object obj = selection.getFirstElement(); |
| if (obj instanceof ISourceReference) { |
| try { |
| ISourceRange range = ((ISourceReference) obj).getSourceRange(); |
| if (range != null) { |
| setSelection(range, !isActivePart()); |
| } |
| } catch (CModelException e) { |
| // Selection change not applied. |
| } |
| } |
| } |
| } |
| |
| /** |
| * Sets selection for C element. |
| * |
| * @param element Element to select. |
| */ |
| public void setSelection(ICElement element) { |
| if (element instanceof ISourceReference && !(element instanceof ITranslationUnit)) { |
| ISourceReference reference = (ISourceReference) element; |
| // set hightlight range |
| setSelection(reference, true); |
| } |
| } |
| |
| /** |
| * Sets selection for source reference. |
| * |
| * @param element Source reference to set. |
| * @param moveCursor Should cursor be moved. |
| */ |
| public void setSelection(ISourceReference element, boolean moveCursor) { |
| if (element != null) { |
| StyledText textWidget = null; |
| |
| ISourceViewer sourceViewer = getSourceViewer(); |
| if (sourceViewer != null) |
| textWidget = sourceViewer.getTextWidget(); |
| |
| if (textWidget == null) |
| return; |
| |
| try { |
| setSelection(element.getSourceRange(), moveCursor); |
| } catch (CModelException e) { |
| // Selection not applied. |
| } |
| } |
| } |
| |
| /** |
| * Sets the current editor selection to the source range. Optionally |
| * sets the current editor position. |
| * |
| * @param element the source range to be shown in the editor, can be null. |
| * @param moveCursor if true the editor is scrolled to show the range. |
| */ |
| public void setSelection(ISourceRange element, 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 (element != null) { |
| StyledText textWidget= null; |
| |
| ISourceViewer sourceViewer= getSourceViewer(); |
| if (sourceViewer == null) |
| return; |
| |
| textWidget= sourceViewer.getTextWidget(); |
| if (textWidget == null) |
| return; |
| |
| try { |
| IRegion alternateRegion = null; |
| int start = element.getStartPos(); |
| int length = element.getLength(); |
| |
| // Sanity check sometimes the parser may throw wrong numbers. |
| if (start < 0 || length < 0) { |
| start = 0; |
| length = 0; |
| } |
| |
| // 0 length and start and non-zero start line says we know |
| // the line for some reason, but not the offset. |
| if (length == 0 && start == 0 && element.getStartLine() > 0) { |
| // We have the information in term of lines, we can work it out. |
| // Binary elements return the first executable statement so we have to subtract -1 |
| start = getDocumentProvider().getDocument(getEditorInput()).getLineOffset(element.getStartLine() - 1); |
| if (element.getEndLine() > 0) { |
| length = getDocumentProvider().getDocument(getEditorInput()).getLineOffset(element.getEndLine()) - start; |
| } else { |
| length = start; |
| } |
| // create an alternate region for the keyword highlight. |
| alternateRegion = getDocumentProvider().getDocument(getEditorInput()).getLineInformation(element.getStartLine() - 1); |
| if (start == length || length < 0) { |
| if (alternateRegion != null) { |
| start = alternateRegion.getOffset(); |
| length = alternateRegion.getLength(); |
| } |
| } |
| } |
| setHighlightRange(start, length, moveCursor); |
| |
| if (moveCursor) { |
| start = element.getIdStartPos(); |
| length = element.getIdLength(); |
| if (start == 0 && length == 0 && alternateRegion != null) { |
| start = alternateRegion.getOffset(); |
| length = alternateRegion.getLength(); |
| } |
| if (start > -1 && length > 0) { |
| try { |
| textWidget.setRedraw(false); |
| sourceViewer.revealRange(start, length); |
| sourceViewer.setSelectedRange(start, length); |
| } finally { |
| textWidget.setRedraw(true); |
| } |
| markInNavigationHistory(); |
| } |
| updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_INPUT_POSITION); |
| } |
| } catch (IllegalArgumentException | BadLocationException e) { |
| // No information to the user |
| } |
| } else if (moveCursor) { |
| resetHighlightRange(); |
| markInNavigationHistory(); |
| } |
| } |
| |
| /** |
| * Checks is the editor active part. |
| * @return {@code true} if editor is the active part of the workbench. |
| */ |
| private boolean isActivePart() { |
| IWorkbenchWindow window = getSite().getWorkbenchWindow(); |
| IPartService service = window.getPartService(); |
| return (this == service.getActivePart()); |
| } |
| |
| @Override |
| protected void installTabsToSpacesConverter() { |
| ISourceViewer sourceViewer= getSourceViewer(); |
| SourceViewerConfiguration config= getSourceViewerConfiguration(); |
| if (config != null && sourceViewer instanceof ITextViewerExtension7) { |
| int tabWidth= config.getTabWidth(sourceViewer); |
| TabsToSpacesConverter tabToSpacesConverter= new TabsToSpacesConverter(); |
| tabToSpacesConverter.setNumberOfSpacesPerTab(tabWidth); |
| IDocumentProvider provider= getDocumentProvider(); |
| if (provider instanceof CDocumentProvider) { |
| CDocumentProvider cProvider= (CDocumentProvider) provider; |
| tabToSpacesConverter.setLineTracker(cProvider.createLineTracker(getEditorInput())); |
| } else { |
| tabToSpacesConverter.setLineTracker(new DefaultLineTracker()); |
| } |
| ((ITextViewerExtension7) sourceViewer).setTabsToSpacesConverter(tabToSpacesConverter); |
| updateIndentationMode(); |
| } |
| } |
| |
| private void updateIndentationMode() { |
| ISourceViewer sourceViewer= getSourceViewer(); |
| if (sourceViewer instanceof CSourceViewer) { |
| CSourceViewer cSourceVieer= (CSourceViewer) sourceViewer; |
| ICElement element= getInputCElement(); |
| ICProject project= element == null ? null : element.getCProject(); |
| final int indentWidth= CodeFormatterUtil.getIndentWidth(project); |
| final boolean useSpaces= isTabsToSpacesConversionEnabled(); |
| cSourceVieer.configureIndentation(indentWidth, useSpaces); |
| } |
| super.updateIndentPrefixes(); |
| } |
| |
| @Override |
| protected boolean isTabsToSpacesConversionEnabled() { |
| ICElement element= getInputCElement(); |
| ICProject project= element == null ? null : element.getCProject(); |
| String option; |
| if (project == null) |
| option= CCorePlugin.getOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR); |
| else |
| option= project.getOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, true); |
| return CCorePlugin.SPACE.equals(option); |
| } |
| |
| protected void uninstallProjectionModelUpdater() { |
| if (fProjectionModelUpdater != null) { |
| fProjectionModelUpdater.uninstall(); |
| fProjectionModelUpdater = null; |
| } |
| } |
| |
| /** |
| * @see org.eclipse.ui.IWorkbenchPart#dispose() |
| */ |
| @Override |
| public void dispose() { |
| fIndexUpdateRequestor.updateIndexInclusion(null); |
| |
| ISourceViewer sourceViewer = getSourceViewer(); |
| if (sourceViewer instanceof ITextViewerExtension) |
| ((ITextViewerExtension) sourceViewer).removeVerifyKeyListener(fBracketInserter); |
| |
| uninstallProjectionModelUpdater(); |
| |
| if (fProjectionSupport != null) { |
| fProjectionSupport.dispose(); |
| fProjectionSupport = null; |
| } |
| |
| // Cancel possible running computation |
| fMarkOccurrenceAnnotations= false; |
| uninstallOccurrencesFinder(); |
| uninstallOverrideIndicator(); |
| |
| uninstallSemanticHighlighting(); |
| |
| if (fCEditorErrorTickUpdater != null) { |
| fCEditorErrorTickUpdater.dispose(); |
| fCEditorErrorTickUpdater = null; |
| } |
| |
| if (fBracketMatcher != null) { |
| fBracketMatcher.dispose(); |
| fBracketMatcher = null; |
| } |
| |
| if (fOutlinePage != null) { |
| fOutlinePage.dispose(); |
| fOutlinePage = null; |
| } |
| |
| if (fSelectionSearchGroup != null) { |
| fSelectionSearchGroup.dispose(); |
| fSelectionSearchGroup = null; |
| } |
| |
| if (fTextSearchGroup != null) { |
| fTextSearchGroup.dispose(); |
| fTextSearchGroup = null; |
| } |
| |
| if (fRefactoringActionGroup != null) { |
| fRefactoringActionGroup.dispose(); |
| fRefactoringActionGroup = null; |
| } |
| |
| if (fOpenInViewGroup != null) { |
| fOpenInViewGroup.dispose(); |
| fOpenInViewGroup = null; |
| } |
| |
| if (fGenerateActionGroup != null) { |
| fGenerateActionGroup.dispose(); |
| fGenerateActionGroup= null; |
| } |
| |
| if (fSurroundWithActionGroup != null) { |
| fSurroundWithActionGroup.dispose(); |
| fSurroundWithActionGroup= null; |
| } |
| |
| if (fEditorSelectionChangedListener != null) { |
| fEditorSelectionChangedListener.uninstall(getSelectionProvider()); |
| fEditorSelectionChangedListener = null; |
| } |
| |
| if (fSelectionHistory != null) { |
| fSelectionHistory.dispose(); |
| fSelectionHistory = null; |
| } |
| |
| super.dispose(); |
| } |
| |
| @Override |
| protected boolean canHandleMove(IEditorInput originalElement, IEditorInput movedElement) { |
| String oldLanguage = ""; //$NON-NLS-1$ |
| IFile originalFile = getEditorInputFile(originalElement); |
| |
| if (originalFile != null) { |
| // If the project of the original input cannot be accessed, the project is being |
| // renamed - accept the move. See http://bugs.eclipse.org/434852 |
| if (originalFile.getProject() != null && !originalFile.getProject().isAccessible()) { |
| return true; |
| } |
| IContentType type = CCorePlugin.getContentType(originalFile.getProject(), originalFile.getName()); |
| if (type != null) { |
| oldLanguage = type.getId(); |
| } |
| if (oldLanguage == null) { |
| return false; |
| } |
| } |
| |
| String newLanguage = ""; //$NON-NLS-1$ |
| IFile movedFile = getEditorInputFile(movedElement); |
| if (movedFile != null) { |
| IContentType type = CCorePlugin.getContentType(movedFile.getProject(), movedFile.getName()); |
| if (type != null) { |
| newLanguage = type.getId(); |
| } |
| if (newLanguage == null) { |
| return false; |
| } |
| } |
| return oldLanguage.equals(newLanguage); |
| } |
| |
| private IFile getEditorInputFile(IEditorInput editorInput) { |
| if (editorInput instanceof IFileEditorInput) { |
| return ((IFileEditorInput) editorInput).getFile(); |
| } |
| return null; |
| } |
| |
| @Override |
| protected void createActions() { |
| super.createActions(); |
| |
| fFoldingGroup = new FoldingActionGroup(this, getSourceViewer()); |
| |
| // Default text editing menu items |
| IAction action= new GotoMatchingBracketAction(this); |
| action.setActionDefinitionId(ICEditorActionDefinitionIds.GOTO_MATCHING_BRACKET); |
| setAction(GotoMatchingBracketAction.GOTO_MATCHING_BRACKET, action); |
| |
| final ResourceBundle bundle = ConstructedCEditorMessages.getResourceBundle(); |
| action = new GotoNextBookmarkAction(bundle, "GotoNextBookmark.", this); //$NON-NLS-1$ |
| action.setActionDefinitionId(ICEditorActionDefinitionIds.GOTO_NEXT_BOOKMARK); |
| setAction(GotoNextBookmarkAction.NEXT_BOOKMARK, action); |
| |
| action = new FindWordAction(bundle, "FindWord.", this, getSourceViewer()); //$NON-NLS-1$ |
| action.setActionDefinitionId(ICEditorActionDefinitionIds.FIND_WORD); |
| setAction(FindWordAction.FIND_WORD, action); |
| markAsStateDependentAction(FindWordAction.FIND_WORD, true); |
| markAsSelectionDependentAction(FindWordAction.FIND_WORD, true); |
| |
| action = new ToggleCommentAction(bundle, "ToggleComment.", this); //$NON-NLS-1$ |
| action.setActionDefinitionId(ICEditorActionDefinitionIds.TOGGLE_COMMENT); |
| setAction("ToggleComment", action); //$NON-NLS-1$ |
| markAsStateDependentAction("ToggleComment", true); //$NON-NLS-1$ |
| configureToggleCommentAction(); |
| |
| action = new AddBlockCommentAction(bundle, "AddBlockComment.", this); //$NON-NLS-1$ |
| action.setActionDefinitionId(ICEditorActionDefinitionIds.ADD_BLOCK_COMMENT); |
| setAction("AddBlockComment", action); //$NON-NLS-1$ |
| markAsStateDependentAction("AddBlockComment", true); //$NON-NLS-1$ |
| markAsSelectionDependentAction("AddBlockComment", true); //$NON-NLS-1$ |
| //WorkbenchHelp.setHelp(action, ICHelpContextIds.ADD_BLOCK_COMMENT_ACTION); |
| |
| action = new RemoveBlockCommentAction(bundle, "RemoveBlockComment.", this); //$NON-NLS-1$ |
| action.setActionDefinitionId(ICEditorActionDefinitionIds.REMOVE_BLOCK_COMMENT); |
| setAction("RemoveBlockComment", action); //$NON-NLS-1$ |
| markAsStateDependentAction("RemoveBlockComment", true); //$NON-NLS-1$ |
| markAsSelectionDependentAction("RemoveBlockComment", true); //$NON-NLS-1$ |
| //WorkbenchHelp.setHelp(action, ICHelpContextIds.REMOVE_BLOCK_COMMENT_ACTION); |
| |
| action = new IndentAction(bundle, "Indent.", this, false); //$NON-NLS-1$ |
| action.setActionDefinitionId(ICEditorActionDefinitionIds.INDENT); |
| setAction("Indent", action); //$NON-NLS-1$ |
| markAsStateDependentAction("Indent", true); //$NON-NLS-1$ |
| markAsSelectionDependentAction("Indent", true); //$NON-NLS-1$ |
| // PlatformUI.getWorkbench().getHelpSystem().setHelp(action, ICHelpContextIds.INDENT_ACTION); |
| |
| action = new AlignConstAction(bundle, "AlignConst.", this); //$NON-NLS-1$ |
| action.setActionDefinitionId(ICEditorActionDefinitionIds.ALIGN_CONST); |
| setAction("AlignConst", action); //$NON-NLS-1$ |
| markAsStateDependentAction("AlignConst", true); //$NON-NLS-1$ |
| markAsSelectionDependentAction("AlignConst", true); //$NON-NLS-1$ |
| |
| action = new IndentAction(bundle, "Indent.", this, true); //$NON-NLS-1$ |
| setAction("IndentOnTab", action); //$NON-NLS-1$ |
| markAsStateDependentAction("IndentOnTab", true); //$NON-NLS-1$ |
| markAsSelectionDependentAction("IndentOnTab", true); //$NON-NLS-1$ |
| |
| if (getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_SMART_TAB)) { |
| setActionActivationCode("IndentOnTab", '\t', -1, SWT.NONE); //$NON-NLS-1$ |
| } |
| |
| action = new TextOperationAction(bundle, "Format.", this, ISourceViewer.FORMAT); //$NON-NLS-1$ |
| action.setActionDefinitionId(ICEditorActionDefinitionIds.FORMAT); |
| setAction("Format", action); //$NON-NLS-1$ |
| markAsStateDependentAction("Format", true); //$NON-NLS-1$ |
| |
| action = new SortLinesAction(this); |
| action.setActionDefinitionId(ICEditorActionDefinitionIds.SORT_LINES); |
| setAction("SortLines", action); //$NON-NLS-1$ |
| markAsStateDependentAction("SortLines", true); //$NON-NLS-1$ |
| markAsSelectionDependentAction("SortLines", true); //$NON-NLS-1$ |
| |
| action = new ContentAssistAction(bundle, "ContentAssistProposal.", this); //$NON-NLS-1$ |
| action.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS); |
| setAction("ContentAssistProposal", action); //$NON-NLS-1$ |
| markAsStateDependentAction("ContentAssistProposal", true); //$NON-NLS-1$ |
| |
| action= new TextOperationAction(bundle, "ContentAssistContextInformation.", this, ISourceViewer.CONTENTASSIST_CONTEXT_INFORMATION); //$NON-NLS-1$ |
| action.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_CONTEXT_INFORMATION); |
| setAction("ContentAssistContextInformation", action); //$NON-NLS-1$ |
| markAsStateDependentAction("ContentAssistContextInformation", true); //$NON-NLS-1$ |
| |
| action = new TextOperationAction(bundle, "OpenOutline.", this, CSourceViewer.SHOW_OUTLINE, true); //$NON-NLS-1$ |
| action.setActionDefinitionId(ICEditorActionDefinitionIds.OPEN_OUTLINE); |
| setAction("OpenOutline", action); //$NON-NLS-1$*/ |
| |
| action = new TextOperationAction(bundle, "OpenHierarchy.", this, CSourceViewer.SHOW_HIERARCHY, true); //$NON-NLS-1$ |
| action.setActionDefinitionId(ICEditorActionDefinitionIds.OPEN_QUICK_TYPE_HIERARCHY); |
| setAction("OpenHierarchy", action); //$NON-NLS-1$*/ |
| |
| action = new GoToNextPreviousMemberAction(bundle, "GotoNextMember.", this, true); //$NON-NLS-1$ |
| action.setActionDefinitionId(ICEditorActionDefinitionIds.GOTO_NEXT_MEMBER); |
| setAction(GoToNextPreviousMemberAction.PREVIOUS_MEMBER, action); |
| |
| action = new GoToNextPreviousMemberAction(bundle, "GotoPreviousMember.", this, false); //$NON-NLS-1$ |
| action.setActionDefinitionId(ICEditorActionDefinitionIds.GOTO_PREVIOUS_MEMBER); |
| setAction(GoToNextPreviousMemberAction.NEXT_MEMBER, action); |
| |
| action= new ToggleSourceAndHeaderAction(bundle, "ToggleSourceHeader.", this); //$NON-NLS-1$ |
| action.setActionDefinitionId(ICEditorActionDefinitionIds.TOGGLE_SOURCE_HEADER); |
| setAction("ToggleSourceHeader", action); //$NON-NLS-1$ |
| |
| action = new TextOperationAction(bundle, "OpenMacroExplorer.", this, CSourceViewer.SHOW_MACRO_EXPLORER, true); //$NON-NLS-1$ |
| action.setActionDefinitionId(ICEditorActionDefinitionIds.OPEN_QUICK_MACRO_EXPLORER); |
| setAction("OpenMacroExplorer", action); //$NON-NLS-1$*/ |
| |
| fSelectionHistory = new SelectionHistory(this); |
| |
| action = new StructureSelectEnclosingAction(bundle, this, fSelectionHistory); |
| action.setActionDefinitionId(ICEditorActionDefinitionIds.SELECT_ENCLOSING); |
| setAction(StructureSelectionAction.ENCLOSING, action); |
| |
| action = new StructureSelectNextAction(bundle, this, fSelectionHistory); |
| action.setActionDefinitionId(ICEditorActionDefinitionIds.SELECT_NEXT); |
| setAction(StructureSelectionAction.NEXT, action); |
| |
| action = new StructureSelectPreviousAction(bundle, this, fSelectionHistory); |
| action.setActionDefinitionId(ICEditorActionDefinitionIds.SELECT_PREVIOUS); |
| setAction(StructureSelectionAction.PREVIOUS, action); |
| |
| action = new StructureSelectHistoryAction(bundle, this, fSelectionHistory); |
| action.setActionDefinitionId(ICEditorActionDefinitionIds.SELECT_LAST); |
| setAction(StructureSelectionAction.HISTORY, action); |
| |
| // add annotation actions for roll-over expand hover |
| action= new CSelectMarkerRulerAction(bundle, "Editor.RulerAnnotationSelection.", this); //$NON-NLS-1$ |
| setAction("AnnotationAction", action); //$NON-NLS-1$ |
| |
| |
| // Assorted action groupings |
| fSelectionSearchGroup = createSelectionSearchGroup(); |
| fTextSearchGroup= new TextSearchGroup(this); |
| fRefactoringActionGroup= new CRefactoringActionGroup(this, ITextEditorActionConstants.GROUP_EDIT); |
| fOpenInViewGroup= createOpenViewActionGroup(); |
| fGenerateActionGroup= new GenerateActionGroup(this, ITextEditorActionConstants.GROUP_EDIT); |
| fSurroundWithActionGroup = new SurroundWithActionGroup(this, ITextEditorActionConstants.GROUP_EDIT); |
| |
| action = getAction(ITextEditorActionConstants.SHIFT_RIGHT); |
| if (action != null) { |
| action.setId(ITextEditorActionConstants.SHIFT_RIGHT); |
| CPluginImages.setImageDescriptors(action, CPluginImages.T_LCL, CPluginImages.IMG_MENU_SHIFT_RIGHT); |
| } |
| action = getAction(ITextEditorActionConstants.SHIFT_LEFT); |
| if (action != null) { |
| action.setId(ITextEditorActionConstants.SHIFT_LEFT); |
| CPluginImages.setImageDescriptors(action, CPluginImages.T_LCL, CPluginImages.IMG_MENU_SHIFT_LEFT); |
| } |
| } |
| |
| protected ActionGroup createSelectionSearchGroup() { |
| return new SelectionSearchGroup(this); |
| } |
| |
| protected ActionGroup createOpenViewActionGroup() { |
| return new OpenViewActionGroup(this); |
| } |
| |
| @Override |
| public void editorContextMenuAboutToShow(IMenuManager menu) { |
| // marker for contributions to the top |
| menu.add(new GroupMarker(ICommonMenuConstants.GROUP_TOP)); |
| // separator for debug related actions (similar to ruler context menu) |
| menu.add(new Separator(IContextMenuConstants.GROUP_DEBUG)); |
| menu.add(new GroupMarker(IContextMenuConstants.GROUP_DEBUG+".end")); //$NON-NLS-1$ |
| |
| super.editorContextMenuAboutToShow(menu); |
| |
| // remove shift actions added by base class |
| menu.remove(ITextEditorActionConstants.SHIFT_LEFT); |
| menu.remove(ITextEditorActionConstants.SHIFT_RIGHT); |
| |
| menu.insertAfter(IContextMenuConstants.GROUP_OPEN, new GroupMarker(IContextMenuConstants.GROUP_SHOW)); |
| |
| final boolean hasCElement= getInputCElement() != null; |
| if (hasCElement) { |
| addAction(menu, IContextMenuConstants.GROUP_OPEN, "OpenDeclarations"); //$NON-NLS-1$ |
| addAction(menu, IContextMenuConstants.GROUP_OPEN, "OpenDefinition"); //$NON-NLS-1$ |
| addAction(menu, IContextMenuConstants.GROUP_OPEN, "OpenTypeHierarchy"); //$NON-NLS-1$ |
| addAction(menu, IContextMenuConstants.GROUP_OPEN, "OpenCallHierarchy"); //$NON-NLS-1$ |
| |
| addAction(menu, IContextMenuConstants.GROUP_OPEN, "OpenOutline"); //$NON-NLS-1$ |
| addAction(menu, IContextMenuConstants.GROUP_OPEN, "OpenHierarchy"); //$NON-NLS-1$ |
| addAction(menu, IContextMenuConstants.GROUP_OPEN, "OpenMacroExplorer"); //$NON-NLS-1$ |
| addAction(menu, IContextMenuConstants.GROUP_OPEN, "ToggleSourceHeader"); //$NON-NLS-1$ |
| } |
| |
| ActionContext context= new ActionContext(getSelectionProvider().getSelection()); |
| fGenerateActionGroup.setContext(context); |
| fGenerateActionGroup.fillContextMenu(menu); |
| fGenerateActionGroup.setContext(null); |
| |
| fSurroundWithActionGroup.setContext(context); |
| fSurroundWithActionGroup.fillContextMenu(menu); |
| fSurroundWithActionGroup.setContext(null); |
| |
| if (hasCElement) { |
| fSelectionSearchGroup.fillContextMenu(menu); |
| } |
| fTextSearchGroup.fillContextMenu(menu); |
| |
| if (hasCElement) { |
| fRefactoringActionGroup.fillContextMenu(menu); |
| fOpenInViewGroup.fillContextMenu(menu); |
| } |
| } |
| |
| @Override |
| protected void rulerContextMenuAboutToShow(IMenuManager menu) { |
| super.rulerContextMenuAboutToShow(menu); |
| IMenuManager foldingMenu= new MenuManager(CEditorMessages.CEditor_menu_folding, "projection"); //$NON-NLS-1$ |
| menu.appendToGroup(ITextEditorActionConstants.GROUP_RULERS, foldingMenu); |
| |
| IAction action= getAction("FoldingToggle"); //$NON-NLS-1$ |
| foldingMenu.add(action); |
| action= getAction("FoldingExpandAll"); //$NON-NLS-1$ |
| foldingMenu.add(action); |
| action= getAction("FoldingCollapseAll"); //$NON-NLS-1$ |
| foldingMenu.add(action); |
| action= getAction("FoldingRestore"); //$NON-NLS-1$ |
| foldingMenu.add(action); |
| } |
| |
| /** |
| * Sets an input for the outline page. |
| * @param page Page to set the input. |
| * @param input Input to set. |
| */ |
| public static void setOutlinePageInput(CContentOutlinePage page, IEditorInput input) { |
| if (page != null) { |
| IWorkingCopyManager manager = CUIPlugin.getDefault().getWorkingCopyManager(); |
| page.setInput(manager.getWorkingCopy(input)); |
| } |
| } |
| |
| private static void setOutlinePageInputIfNotSame(CContentOutlinePage page, IEditorInput input) { |
| if (page != null) { |
| IWorkingCopyManager manager = CUIPlugin.getDefault().getWorkingCopyManager(); |
| IWorkingCopy workingCopy = manager.getWorkingCopy(input); |
| if (workingCopy != page.getRoot()) { |
| page.setInput(workingCopy); |
| } |
| } |
| } |
| |
| /** |
| * Determines if folding is enabled. |
| * @return {@code true} if folding is enabled, {@code false} otherwise. |
| */ |
| protected boolean isFoldingEnabled() { |
| return CUIPlugin.getDefault().getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_FOLDING_ENABLED); |
| } |
| |
| @Override |
| public int getOrientation() { |
| // C/C++ editors are always left to right by default |
| return SWT.LEFT_TO_RIGHT; |
| } |
| |
| @Override |
| public void createPartControl(Composite parent) { |
| super.createPartControl(parent); |
| |
| // bug 291008 - register custom help listener |
| final IWorkbenchHelpSystem helpSystem = PlatformUI.getWorkbench().getHelpSystem(); |
| parent.addHelpListener(new HelpListener() { |
| @Override |
| public void helpRequested(HelpEvent e) { |
| IContextProvider provider = CEditor.this.getAdapter(IContextProvider.class); |
| if (provider != null) { |
| IContext context = provider.getContext(CEditor.this); |
| if (context != null) { |
| helpSystem.displayHelp(context); |
| return; |
| } |
| } |
| helpSystem.displayHelp(ICHelpContextIds.CEDITOR_VIEW); |
| }}); |
| |
| fEditorSelectionChangedListener = new EditorSelectionChangedListener(); |
| fEditorSelectionChangedListener.install(getSelectionProvider()); |
| |
| if (isSemanticHighlightingEnabled()) |
| installSemanticHighlighting(); |
| |
| IPreferenceStore preferenceStore = getPreferenceStore(); |
| boolean closeBrackets = preferenceStore.getBoolean(CLOSE_BRACKETS); |
| boolean closeAngularBrackets = preferenceStore.getBoolean(CLOSE_ANGULAR_BRACKETS); |
| boolean closeBraces = preferenceStore.getBoolean(CLOSE_BRACES); |
| boolean closeStrings = preferenceStore.getBoolean(CLOSE_STRINGS); |
| |
| fBracketInserter.setCloseBracketsEnabled(closeBrackets); |
| fBracketInserter.setCloseAngularBracketsEnabled(closeAngularBrackets); |
| fBracketInserter.setCloseAngularBracketsEnabled(closeBraces); |
| fBracketInserter.setCloseStringsEnabled(closeStrings); |
| |
| ISourceViewer sourceViewer = getSourceViewer(); |
| if (sourceViewer instanceof ITextViewerExtension) { |
| fBracketInserter.setSourceViewer(sourceViewer); |
| ((ITextViewerExtension) sourceViewer).prependVerifyKeyListener(fBracketInserter); |
| } |
| |
| if (isMarkingOccurrences()) |
| installOccurrencesFinder(false); |
| |
| if(isShowingOverrideIndicators()) |
| installOverrideIndicator(false); |
| } |
| |
| @Override |
| protected SourceViewerDecorationSupport getSourceViewerDecorationSupport(ISourceViewer viewer) { |
| if (fSourceViewerDecorationSupport == null) { |
| fSourceViewerDecorationSupport= new CSourceViewerDecorationSupport(this, viewer, getOverviewRuler(), getAnnotationAccess(), getSharedColors()); |
| configureSourceViewerDecorationSupport(fSourceViewerDecorationSupport); |
| } |
| return fSourceViewerDecorationSupport; |
| } |
| |
| @Override |
| protected void configureSourceViewerDecorationSupport(SourceViewerDecorationSupport support) { |
| super.configureSourceViewerDecorationSupport(support); |
| // Enhance the stock source viewer decorator with a bracket matcher |
| support.setCharacterPairMatcher(fBracketMatcher); |
| support.setMatchingCharacterPainterPreferenceKeys(MATCHING_BRACKETS, MATCHING_BRACKETS_COLOR); |
| ((CSourceViewerDecorationSupport) support).setInactiveCodePainterPreferenceKeys(INACTIVE_CODE_ENABLE, INACTIVE_CODE_COLOR); |
| |
| // The base class will have already called setMarginPainterPreferenceKeys. We override it |
| // here with more specific values for the C/C++ editor. Note that this needs to go after |
| // the call to super since the last invocation wins. |
| support.setMarginPainterPreferenceKeys( |
| AbstractDecoratedTextEditorPreferenceConstants.EDITOR_PRINT_MARGIN, |
| AbstractDecoratedTextEditorPreferenceConstants.EDITOR_PRINT_MARGIN_COLOR, |
| DefaultCodeFormatterConstants.FORMATTER_LINE_SPLIT); |
| } |
| |
| /** |
| * Returns the lock object for the given annotation model. |
| * |
| * @param annotationModel the annotation model |
| * @return the annotation model's lock object |
| * @since 5.0 |
| */ |
| private Object getLockObject(IAnnotationModel annotationModel) { |
| if (annotationModel instanceof ISynchronizable) { |
| Object lock= ((ISynchronizable) annotationModel).getLockObject(); |
| if (lock != null) |
| return lock; |
| } |
| return annotationModel; |
| } |
| |
| /** |
| * 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(CEditorMessages.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(CEditorMessages.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(CEditorMessages.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()); |
| } |
| |
| protected void updateStatusLine() { |
| ITextSelection selection = (ITextSelection) getSelectionProvider().getSelection(); |
| Annotation annotation = getAnnotation(selection.getOffset(), selection.getLength(), fSyncProblemsViewMarker); |
| setStatusLineErrorMessage(null); |
| setStatusLineMessage(null); |
| if (annotation != null) { |
| if (fSyncProblemsViewMarker == null) { |
| updateMarkerViews(annotation); |
| } |
| if (annotation instanceof ICAnnotation && ((ICAnnotation) annotation).isProblem()) |
| setStatusLineMessage(annotation.getText()); |
| } |
| fSyncProblemsViewMarker = null; |
| } |
| |
| /** |
| * Returns the annotation overlapping with the given range or {@code null}. |
| * |
| * @param offset the region offset |
| * @param length the region length |
| * @param marker associated marker or {@code null} of not available |
| * @return the found annotation or {@code null} |
| */ |
| private Annotation getAnnotation(int offset, int length, IMarker marker) { |
| IAnnotationModel model= getDocumentProvider().getAnnotationModel(getEditorInput()); |
| if (model == null) |
| return null; |
| |
| Iterator<Annotation> parent; |
| if (model instanceof IAnnotationModelExtension2) { |
| parent= ((IAnnotationModelExtension2) model).getAnnotationIterator(offset, length, true, true); |
| } else { |
| parent= model.getAnnotationIterator(); |
| } |
| |
| Iterator<Annotation> e= new CAnnotationIterator(parent, false); |
| Annotation annotation = null; |
| while (e.hasNext()) { |
| Annotation a = e.next(); |
| if (!isNavigationTarget(a)) |
| continue; |
| |
| Position p = model.getPosition(a); |
| if (p != null && p.overlapsWith(offset, length)) { |
| if (annotation == null) { |
| annotation = a; |
| if (marker == null) |
| break; |
| } |
| if (a instanceof MarkerAnnotation) { |
| if (((MarkerAnnotation) a).getMarker().equals(marker)) { |
| annotation = a; |
| break; |
| } |
| } |
| } |
| } |
| |
| return annotation; |
| } |
| |
| /** |
| * Returns the dektop's StatusLineManager |
| */ |
| @Override |
| protected IStatusLineManager getStatusLineManager() { |
| IEditorActionBarContributor contributor = getEditorSite().getActionBarContributor(); |
| if (contributor instanceof EditorActionBarContributor) { |
| return ((EditorActionBarContributor) contributor).getActionBars().getStatusLineManager(); |
| } |
| return null; |
| } |
| |
| /** |
| * Configures the toggle comment action |
| * |
| * @since 4.0.0 |
| */ |
| private void configureToggleCommentAction() { |
| IAction action = getAction("ToggleComment"); //$NON-NLS-1$ |
| if (action instanceof ToggleCommentAction) { |
| ISourceViewer sourceViewer = getSourceViewer(); |
| SourceViewerConfiguration configuration = getSourceViewerConfiguration(); |
| ((ToggleCommentAction) action).configure(sourceViewer, configuration); |
| } |
| } |
| |
| @Override |
| protected void createNavigationActions() { |
| super.createNavigationActions(); |
| |
| final StyledText textWidget = getSourceViewer().getTextWidget(); |
| |
| IAction 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); |
| |
| action = new DeletePreviousSubWordAction(); |
| action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD); |
| setAction(ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD, action); |
| textWidget.setKeyBinding(SWT.CTRL | SWT.BS, SWT.NULL); |
| markAsStateDependentAction(ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD, true); |
| |
| action = new DeleteNextSubWordAction(); |
| action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_NEXT_WORD); |
| setAction(ITextEditorActionDefinitionIds.DELETE_NEXT_WORD, action); |
| textWidget.setKeyBinding(SWT.CTRL | SWT.DEL, SWT.NULL); |
| markAsStateDependentAction(ITextEditorActionDefinitionIds.DELETE_NEXT_WORD, true); |
| } |
| |
| public final ISourceViewer getViewer() { |
| return getSourceViewer(); |
| } |
| |
| @Override |
| protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles) { |
| IPreferenceStore store= getPreferenceStore(); |
| ISourceViewer sourceViewer = |
| new AdaptedSourceViewer(parent, ruler, getOverviewRuler(), isOverviewRulerVisible(), styles, store); |
| |
| CSourceViewer cSourceViewer= null; |
| if (sourceViewer instanceof CSourceViewer) { |
| cSourceViewer= (CSourceViewer) sourceViewer; |
| } |
| |
| /* |
| * This is a performance optimization to reduce the computation of |
| * the text presentation triggered by {@link #setVisibleDocument(IDocument)} |
| */ |
| if (cSourceViewer != null && isFoldingEnabled() && (store == null || !store.getBoolean(PreferenceConstants.EDITOR_SHOW_SEGMENTS))) |
| cSourceViewer.prepareDelayedProjection(); |
| |
| ProjectionViewer projectionViewer = (ProjectionViewer) sourceViewer; |
| |
| fProjectionSupport = new ProjectionSupport(projectionViewer, getAnnotationAccess(), getSharedColors()); |
| fProjectionSupport.addSummarizableAnnotationType("org.eclipse.ui.workbench.texteditor.error"); //$NON-NLS-1$ |
| fProjectionSupport.addSummarizableAnnotationType("org.eclipse.ui.workbench.texteditor.warning"); //$NON-NLS-1$ |
| fProjectionSupport.addSummarizableAnnotationType("org.eclipse.search.results"); //$NON-NLS-1$ |
| fProjectionSupport.setHoverControlCreator(new IInformationControlCreator() { |
| @Override |
| public IInformationControl createInformationControl(Shell shell) { |
| return new SourceViewerInformationControl(shell, false, getOrientation(), null); |
| } |
| }); |
| fProjectionSupport.install(); |
| |
| fProjectionModelUpdater = CUIPlugin.getDefault().getFoldingStructureProviderRegistry().getCurrentFoldingProvider(); |
| if (fProjectionModelUpdater != null) |
| fProjectionModelUpdater.install(this, projectionViewer); |
| |
| if (isFoldingEnabled()) |
| projectionViewer.doOperation(ProjectionViewer.TOGGLE); |
| |
| getSourceViewerDecorationSupport(sourceViewer); |
| |
| return sourceViewer; |
| } |
| |
| /** Outliner context menu Id */ |
| protected String fOutlinerContextMenuId; |
| |
| /** |
| * Holds the current occurrence annotations. |
| * @since 5.0 |
| */ |
| private Annotation[] fOccurrenceAnnotations= null; |
| /** |
| * Tells whether all occurrences of the element at the |
| * current caret location are automatically marked in |
| * this editor. |
| * @since 5.0 |
| */ |
| private boolean fMarkOccurrenceAnnotations; |
| /** |
| * Tells whether the occurrence annotations are sticky |
| * i.e. whether they stay even if there's no valid Java |
| * element at the current caret position. |
| * Only valid if {@link #fMarkOccurrenceAnnotations} is {@code true}. |
| * @since 5.0 |
| */ |
| private boolean fStickyOccurrenceAnnotations; |
| /** |
| * Tells whether to mark overloaded operator occurrences in this editor. |
| * Only valid if {@link #fMarkOccurrenceAnnotations} is {@code true}. |
| * @since 5.3 |
| */ |
| private boolean fMarkOverloadedOperatorOccurrences; |
| /** |
| * The selection used when forcing occurrence marking |
| * through code. |
| * @since 5.0 |
| */ |
| private ISelection fForcedMarkOccurrencesSelection; |
| /** |
| * The document modification stamp at the time when the last |
| * occurrence marking took place. |
| * @since 5.0 |
| */ |
| private long fMarkOccurrenceModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; |
| /** |
| * The region of the word under the caret used to when |
| * computing the current occurrence markings. |
| * @since 5.0 |
| */ |
| private IRegion fMarkOccurrenceTargetRegion; |
| |
| private OccurrencesAnnotationUpdaterJob fOccurrencesAnnotationUpdaterJob; |
| private OccurrencesFinderJobCanceler fOccurrencesFinderJobCanceler; |
| private ISelectionListenerWithAST fPostSelectionListenerWithAST; |
| |
| private OverrideIndicatorManager fOverrideIndicatorManager; |
| |
| /** |
| * Sets the outliner's context menu ID. |
| */ |
| protected void setOutlinerContextMenuId(String menuId) { |
| fOutlinerContextMenuId = menuId; |
| } |
| |
| @Override |
| protected void initializeKeyBindingScopes() { |
| setKeyBindingScopes(new String [] { "org.eclipse.cdt.ui.cEditorScope" }); //$NON-NLS-1$ |
| } |
| |
| @Override |
| protected boolean affectsTextPresentation(PropertyChangeEvent event) { |
| SourceViewerConfiguration configuration = getSourceViewerConfiguration(); |
| if (configuration instanceof CSourceViewerConfiguration) { |
| return ((CSourceViewerConfiguration) configuration).affectsTextPresentation(event); |
| } |
| return false; |
| } |
| |
| /** |
| * Returns the folding action group, or {@code null} if there is none. |
| * |
| * @return the folding action group, or {@code null} if there is none |
| */ |
| protected FoldingActionGroup getFoldingActionGroup() { |
| return fFoldingGroup; |
| } |
| |
| @Override |
| protected void performRevert() { |
| ProjectionViewer projectionViewer = (ProjectionViewer) getSourceViewer(); |
| projectionViewer.setRedraw(false); |
| try { |
| |
| boolean projectionMode = projectionViewer.isProjectionMode(); |
| if (projectionMode) { |
| projectionViewer.disableProjection(); |
| if (fProjectionModelUpdater != null) |
| fProjectionModelUpdater.uninstall(); |
| } |
| |
| super.performRevert(); |
| |
| if (projectionMode) { |
| if (fProjectionModelUpdater != null) |
| fProjectionModelUpdater.install(this, projectionViewer); |
| projectionViewer.enableProjection(); |
| } |
| |
| } finally { |
| projectionViewer.setRedraw(true); |
| } |
| } |
| |
| /** |
| * Sets the given message as error message to this editor's status line. |
| * |
| * @param message message to be set |
| */ |
| @Override |
| public void setStatusLineErrorMessage(String message) { |
| long now= System.currentTimeMillis(); |
| if (message != null || now - fErrorMessageTime > ERROR_MESSAGE_TIMEOUT) { |
| super.setStatusLineErrorMessage(message); |
| fErrorMessageTime= message != null ? now : 0; |
| } |
| } |
| |
| /** |
| * Sets the given message as message to this editor's status line. |
| * |
| * @param message message to be set |
| * @since 3.0 |
| */ |
| @Override |
| protected void setStatusLineMessage(String message) { |
| if (System.currentTimeMillis() - fErrorMessageTime > ERROR_MESSAGE_TIMEOUT) |
| super.setStatusLineMessage(message); |
| } |
| |
| /** |
| * 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); |
| } |
| |
| 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 static char getEscapeCharacter(char character) { |
| switch (character) { |
| case '"': |
| case '\'': |
| return '\\'; |
| default: |
| return 0; |
| } |
| } |
| |
| private static char getPeerCharacter(char character) { |
| switch (character) { |
| case '(': |
| return ')'; |
| |
| case ')': |
| return '('; |
| |
| case '<': |
| return '>'; |
| |
| case '>': |
| return '<'; |
| |
| case '[': |
| return ']'; |
| |
| case ']': |
| return '['; |
| |
| case '{': |
| return '}'; |
| |
| case '}': |
| return '{'; |
| |
| case '"': |
| return character; |
| |
| case '\'': |
| return character; |
| |
| default: |
| throw new IllegalArgumentException(); |
| } |
| } |
| |
| @Override |
| protected String[] collectContextMenuPreferencePages() { |
| // Add C/C++ Editor relevant pages |
| String[] parentPrefPageIds = super.collectContextMenuPreferencePages(); |
| String[] prefPageIds = new String[parentPrefPageIds.length + 13]; |
| int nIds = 0; |
| prefPageIds[nIds++] = "org.eclipse.cdt.ui.preferences.CEditorPreferencePage"; //$NON-NLS-1$ |
| prefPageIds[nIds++] = "org.eclipse.cdt.ui.preferences.CodeAssistPreferencePage"; //$NON-NLS-1$ |
| prefPageIds[nIds++] = "org.eclipse.cdt.ui.preferences.CodeAssistPreferenceAdvanced"; //$NON-NLS-1$ |
| prefPageIds[nIds++] = "org.eclipse.cdt.ui.preferences.HoverPreferencePage"; //$NON-NLS-1$ |
| prefPageIds[nIds++] = "org.eclipse.cdt.ui.preferences.FoldingPreferencePage"; //$NON-NLS-1$ |
| prefPageIds[nIds++] = "org.eclipse.cdt.ui.preferences.MarkOccurrencesPreferencePage"; //$NON-NLS-1$ |
| prefPageIds[nIds++] = "org.eclipse.cdt.ui.preferences.CodeColoringPreferencePage"; //$NON-NLS-1$ |
| prefPageIds[nIds++] = "org.eclipse.cdt.ui.preferences.TemplatePreferencePage"; //$NON-NLS-1$ |
| prefPageIds[nIds++] = "org.eclipse.cdt.ui.preferences.SmartTypingPreferencePage"; //$NON-NLS-1$ |
| prefPageIds[nIds++] = "org.eclipse.cdt.ui.preferences.CodeFormatterPreferencePage"; //$NON-NLS-1$ |
| prefPageIds[nIds++] = "org.eclipse.cdt.ui.preferences.CScalabilityPreferences"; //$NON-NLS-1$ |
| prefPageIds[nIds++] = "org.eclipse.cdt.ui.preferences.SaveActionsPreferencePage"; //$NON-NLS-1$ |
| prefPageIds[nIds++] = "org.eclipse.cdt.codan.ui.preferences.CodanPreferencePage"; //$NON-NLS-1$ |
| System.arraycopy(parentPrefPageIds, 0, prefPageIds, nIds, parentPrefPageIds.length); |
| return prefPageIds; |
| } |
| |
| @Override |
| public void aboutToBeReconciled() { |
| fIsReconciling= true; |
| |
| // Notify AST provider |
| CUIPlugin.getDefault().getASTProvider().aboutToBeReconciled(getInputCElement()); |
| |
| // Notify listeners |
| Object[] listeners = fReconcilingListeners.getListeners(); |
| for (int i = 0, length= listeners.length; i < length; ++i) { |
| ((ICReconcilingListener) listeners[i]).aboutToBeReconciled(); |
| } |
| } |
| |
| @Override |
| public void reconciled(IASTTranslationUnit ast, boolean force, IProgressMonitor progressMonitor) { |
| fIsReconciling= false; |
| |
| CUIPlugin cuiPlugin= CUIPlugin.getDefault(); |
| if (cuiPlugin == null) |
| return; |
| |
| // Always notify AST provider |
| cuiPlugin.getASTProvider().reconciled(ast, getInputCElement(), progressMonitor); |
| |
| // Notify listeners |
| Object[] listeners = fReconcilingListeners.getListeners(); |
| for (int i = 0, length= listeners.length; i < length; ++i) { |
| ((ICReconcilingListener) listeners[i]).reconciled(ast, force, progressMonitor); |
| } |
| } |
| |
| /** |
| * Adds the given listener. |
| * Has no effect if an identical listener was not already registered. |
| * |
| * @param listener The reconcile listener to be added |
| * @since 4.0 |
| */ |
| final public void addReconcileListener(ICReconcilingListener listener) { |
| fReconcilingListeners.add(listener); |
| } |
| |
| /** |
| * Removes the given listener. |
| * Has no effect if an identical listener was not already registered. |
| * |
| * @param listener the reconcile listener to be removed |
| * @since 4.0 |
| */ |
| final public void removeReconcileListener(ICReconcilingListener listener) { |
| fReconcilingListeners.remove(listener); |
| } |
| |
| /** |
| * @return {@code true} if Semantic Highlighting is enabled. |
| * |
| * @since 4.0 |
| */ |
| protected boolean isSemanticHighlightingEnabled() { |
| return SemanticHighlightings.isEnabled(getPreferenceStore()) && !(isEnableScalablilityMode() && getPreferenceStore().getBoolean(PreferenceConstants.SCALABILITY_SEMANTIC_HIGHLIGHT)); |
| } |
| |
| /** |
| * Install Semantic Highlighting. |
| * |
| * @since 4.0 |
| */ |
| private void installSemanticHighlighting() { |
| if (fSemanticManager == null) { |
| fSemanticManager= new SemanticHighlightingManager(); |
| fSemanticManager.install(this, (CSourceViewer) getSourceViewer(), CUIPlugin.getDefault().getTextTools().getColorManager(), getPreferenceStore()); |
| } |
| } |
| |
| /** |
| * Uninstalls semantic highlighting. |
| * |
| * @since 4.0 |
| */ |
| private void uninstallSemanticHighlighting() { |
| if (fSemanticManager != null) { |
| fSemanticManager.uninstall(); |
| fSemanticManager= null; |
| } |
| } |
| |
| /** |
| * Called whenever the editor is activated and allows for registering |
| * action handlers. |
| */ |
| public void fillActionBars(IActionBars actionBars) { |
| fOpenInViewGroup.fillActionBars(actionBars); |
| fRefactoringActionGroup.fillActionBars(actionBars); |
| fGenerateActionGroup.fillActionBars(actionBars); |
| fFoldingGroup.updateActionBars(); |
| fSurroundWithActionGroup.fillActionBars(actionBars); |
| } |
| |
| @Override |
| protected void updateStateDependentActions() { |
| super.updateStateDependentActions(); |
| fGenerateActionGroup.editorStateChanged(); |
| } |
| |
| /** |
| * Resets the foldings structure according to the folding |
| * preferences. |
| * |
| * @since 4.0 |
| */ |
| public void resetProjection() { |
| if (fProjectionModelUpdater != null) { |
| fProjectionModelUpdater.initialize(); |
| } |
| } |
| |
| /** |
| * Updates occurrence annotations. |
| * |
| * @since 5.0 |
| */ |
| class OccurrencesAnnotationUpdaterJob extends Job { |
| private final IDocument fDocument; |
| private final ISelection fSelection; |
| private final ISelectionValidator fPostSelectionValidator; |
| private boolean fCanceled; |
| private final OccurrenceLocation[] fLocations; |
| |
| public OccurrencesAnnotationUpdaterJob(IDocument document, OccurrenceLocation[] locations, ISelection selection, ISelectionValidator validator) { |
| super(CEditorMessages.CEditor_markOccurrences_job_name); |
| fDocument= document; |
| fSelection= selection; |
| fLocations= locations; |
| fPostSelectionValidator= validator; |
| } |
| |
| // cannot use cancel() because it is declared final |
| void doCancel() { |
| fCanceled= true; |
| cancel(); |
| } |
| |
| private boolean isCanceled(IProgressMonitor progressMonitor) { |
| return fCanceled || progressMonitor.isCanceled() |
| || fPostSelectionValidator != null && !(fPostSelectionValidator.isValid(fSelection) || fForcedMarkOccurrencesSelection == fSelection) |
| || LinkedModeModel.hasInstalledModel(fDocument); |
| } |
| |
| @Override |
| public IStatus run(IProgressMonitor progressMonitor) { |
| if (isCanceled(progressMonitor)) |
| return Status.CANCEL_STATUS; |
| |
| ITextViewer textViewer= getViewer(); |
| if (textViewer == null) |
| return Status.CANCEL_STATUS; |
| |
| IDocument document= textViewer.getDocument(); |
| if (document == null) |
| return Status.CANCEL_STATUS; |
| |
| IDocumentProvider documentProvider= getDocumentProvider(); |
| if (documentProvider == null) |
| return Status.CANCEL_STATUS; |
| |
| IAnnotationModel annotationModel= documentProvider.getAnnotationModel(getEditorInput()); |
| if (annotationModel == null) |
| return Status.CANCEL_STATUS; |
| |
| // Add occurrence annotations |
| int length= fLocations.length; |
| Map<Annotation, Position> annotationMap= new HashMap<>(length); |
| for (int i= 0; i < length; i++) { |
| if (isCanceled(progressMonitor)) |
| return Status.CANCEL_STATUS; |
| |
| OccurrenceLocation location= fLocations[i]; |
| Position position= new Position(location.getOffset(), location.getLength()); |
| |
| String description= location.getDescription(); |
| String annotationType= (location.getFlags() == IOccurrencesFinder.F_WRITE_OCCURRENCE) ? "org.eclipse.cdt.ui.occurrences.write" : "org.eclipse.cdt.ui.occurrences"; //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| annotationMap.put(new Annotation(annotationType, false, description), position); |
| } |
| |
| if (isCanceled(progressMonitor)) |
| return Status.CANCEL_STATUS; |
| |
| synchronized (getLockObject(annotationModel)) { |
| if (annotationModel instanceof IAnnotationModelExtension) { |
| ((IAnnotationModelExtension) annotationModel).replaceAnnotations(fOccurrenceAnnotations, annotationMap); |
| } else { |
| removeOccurrenceAnnotations(); |
| Iterator<Map.Entry<Annotation, Position>> iter= annotationMap.entrySet().iterator(); |
| while (iter.hasNext()) { |
| Map.Entry<Annotation, Position> mapEntry= iter.next(); |
| annotationModel.addAnnotation(mapEntry.getKey(), mapEntry.getValue()); |
| } |
| } |
| fOccurrenceAnnotations= annotationMap.keySet().toArray(new Annotation[annotationMap.keySet().size()]); |
| } |
| |
| return Status.OK_STATUS; |
| } |
| } |
| |
| /** |
| * Cancels the occurrences finder job upon document changes. |
| * |
| * @since 5.0 |
| */ |
| class OccurrencesFinderJobCanceler implements IDocumentListener, ITextInputListener { |
| |
| public void install() { |
| ISourceViewer sourceViewer= getSourceViewer(); |
| if (sourceViewer == null) |
| return; |
| |
| StyledText text= sourceViewer.getTextWidget(); |
| if (text == null || text.isDisposed()) |
| return; |
| |
| sourceViewer.addTextInputListener(this); |
| |
| IDocument document= sourceViewer.getDocument(); |
| if (document != null) |
| document.addDocumentListener(this); |
| } |
| |
| public void uninstall() { |
| ISourceViewer sourceViewer= getSourceViewer(); |
| if (sourceViewer != null) |
| sourceViewer.removeTextInputListener(this); |
| |
| IDocumentProvider documentProvider= getDocumentProvider(); |
| if (documentProvider != null) { |
| IDocument document= documentProvider.getDocument(getEditorInput()); |
| if (document != null) |
| document.removeDocumentListener(this); |
| } |
| } |
| |
| @Override |
| public void documentAboutToBeChanged(DocumentEvent event) { |
| if (fOccurrencesAnnotationUpdaterJob != null) |
| fOccurrencesAnnotationUpdaterJob.doCancel(); |
| } |
| |
| @Override |
| public void documentChanged(DocumentEvent event) { |
| } |
| |
| @Override |
| public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) { |
| if (oldInput == null) |
| return; |
| |
| oldInput.removeDocumentListener(this); |
| } |
| |
| @Override |
| public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { |
| if (newInput == null) |
| return; |
| newInput.addDocumentListener(this); |
| } |
| } |
| |
| /** |
| * Updates the occurrences annotations based |
| * on the current selection. |
| * |
| * @param selection the text selection |
| * @param astRoot the compilation unit AST |
| * @since 5.0 |
| */ |
| protected void updateOccurrenceAnnotations(ITextSelection selection, IASTTranslationUnit astRoot) { |
| if (fOccurrencesAnnotationUpdaterJob != null) |
| fOccurrencesAnnotationUpdaterJob.cancel(); |
| |
| if (!fMarkOccurrenceAnnotations) |
| return; |
| |
| if (astRoot == null || selection == null) |
| return; |
| |
| IDocument document= getSourceViewer().getDocument(); |
| if (document == null) |
| return; |
| |
| ISelectionValidator validator= null; |
| if (fForcedMarkOccurrencesSelection != selection && getSelectionProvider() instanceof ISelectionValidator) { |
| validator= (ISelectionValidator) getSelectionProvider(); |
| if (!validator.isValid(selection)) { |
| return; |
| } |
| } |
| |
| boolean hasChanged= false; |
| if (document instanceof IDocumentExtension4) { |
| int offset= selection.getOffset(); |
| long currentModificationStamp= ((IDocumentExtension4) document).getModificationStamp(); |
| IRegion markOccurrenceTargetRegion= fMarkOccurrenceTargetRegion; |
| hasChanged= currentModificationStamp != fMarkOccurrenceModificationStamp; |
| if (markOccurrenceTargetRegion != null && !hasChanged) { |
| if (markOccurrenceTargetRegion.getOffset() <= offset && offset <= markOccurrenceTargetRegion.getOffset() + markOccurrenceTargetRegion.getLength()) |
| return; |
| } |
| fMarkOccurrenceTargetRegion= CWordFinder.findWord(document, offset); |
| fMarkOccurrenceModificationStamp= currentModificationStamp; |
| } |
| |
| OccurrenceLocation[] locations= null; |
| |
| IASTNodeSelector selector= astRoot.getNodeSelector(null); |
| IASTName name= selector.findEnclosingName(selection.getOffset(), selection.getLength()); |
| if (name == null) |
| name = selector.findEnclosingImplicitName(selection.getOffset(), selection.getLength()); |
| |
| if (validator != null && !validator.isValid(selection)) { |
| return; |
| } |
| |
| if (name != null) { |
| IBinding binding= name.resolveBinding(); |
| if (binding != null) { |
| OccurrencesFinder occurrencesFinder= new OccurrencesFinder(); |
| if (occurrencesFinder.initialize(astRoot, name) == null) { |
| if (!fMarkOverloadedOperatorOccurrences) { |
| occurrencesFinder.setOptions(OccurrencesFinder.OPTION_EXCLUDE_IMPLICIT_REFERENCES); |
| } |
| locations= occurrencesFinder.getOccurrences(); |
| } |
| } |
| } |
| |
| if (locations == null || locations.length == 0) { |
| if (!fStickyOccurrenceAnnotations) |
| removeOccurrenceAnnotations(); |
| else if (hasChanged) // check consistency of current annotations |
| removeOccurrenceAnnotations(); |
| return; |
| } |
| |
| fOccurrencesAnnotationUpdaterJob= new OccurrencesAnnotationUpdaterJob(document, locations, selection, validator); |
| // we are already in a background job |
| //fOccurrencesFinderJob.setPriority(Job.DECORATE); |
| //fOccurrencesFinderJob.setSystem(true); |
| //fOccurrencesFinderJob.schedule(); |
| fOccurrencesAnnotationUpdaterJob.run(new NullProgressMonitor()); |
| } |
| |
| protected void installOccurrencesFinder(boolean forceUpdate) { |
| fMarkOccurrenceAnnotations= true; |
| |
| fPostSelectionListenerWithAST= new ISelectionListenerWithAST() { |
| @Override |
| public void selectionChanged(IEditorPart part, ITextSelection selection, IASTTranslationUnit astRoot) { |
| updateOccurrenceAnnotations(selection, astRoot); |
| } |
| }; |
| SelectionListenerWithASTManager.getDefault().addListener(this, fPostSelectionListenerWithAST); |
| if (forceUpdate && getSelectionProvider() != null) { |
| ICElement inputCElement = getInputCElement(); |
| if (inputCElement instanceof ITranslationUnit) { |
| fForcedMarkOccurrencesSelection= getSelectionProvider().getSelection(); |
| ASTProvider.getASTProvider().runOnAST(inputCElement, ASTProvider.WAIT_NO, getProgressMonitor(), new ASTRunnable() { |
| @Override |
| public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) throws CoreException { |
| updateOccurrenceAnnotations((ITextSelection) fForcedMarkOccurrencesSelection, ast); |
| return Status.OK_STATUS; |
| } |
| }); |
| } |
| } |
| |
| if (fOccurrencesFinderJobCanceler == null) { |
| fOccurrencesFinderJobCanceler= new OccurrencesFinderJobCanceler(); |
| fOccurrencesFinderJobCanceler.install(); |
| } |
| } |
| |
| protected void uninstallOccurrencesFinder() { |
| fMarkOccurrenceAnnotations= false; |
| |
| if (fOccurrencesAnnotationUpdaterJob != null) { |
| fOccurrencesAnnotationUpdaterJob.cancel(); |
| fOccurrencesAnnotationUpdaterJob= null; |
| } |
| |
| if (fOccurrencesFinderJobCanceler != null) { |
| fOccurrencesFinderJobCanceler.uninstall(); |
| fOccurrencesFinderJobCanceler= null; |
| } |
| |
| if (fPostSelectionListenerWithAST != null) { |
| SelectionListenerWithASTManager.getDefault().removeListener(this, fPostSelectionListenerWithAST); |
| fPostSelectionListenerWithAST= null; |
| } |
| |
| removeOccurrenceAnnotations(); |
| } |
| |
| protected boolean isMarkingOccurrences() { |
| IPreferenceStore store= getPreferenceStore(); |
| return store != null && store.getBoolean(PreferenceConstants.EDITOR_MARK_OCCURRENCES); |
| } |
| |
| void removeOccurrenceAnnotations() { |
| fMarkOccurrenceModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; |
| fMarkOccurrenceTargetRegion= null; |
| |
| IDocumentProvider documentProvider= getDocumentProvider(); |
| if (documentProvider == null) |
| return; |
| |
| IAnnotationModel annotationModel= documentProvider.getAnnotationModel(getEditorInput()); |
| if (annotationModel == null || fOccurrenceAnnotations == null) |
| return; |
| |
| synchronized (getLockObject(annotationModel)) { |
| if (annotationModel instanceof IAnnotationModelExtension) { |
| ((IAnnotationModelExtension) annotationModel).replaceAnnotations(fOccurrenceAnnotations, null); |
| } else { |
| for (Annotation occurrenceAnnotation : fOccurrenceAnnotations) |
| annotationModel.removeAnnotation(occurrenceAnnotation); |
| } |
| fOccurrenceAnnotations= null; |
| } |
| } |
| |
| /** |
| * Creates and returns the preference store for this 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<>(3); |
| |
| ICProject project= EditorUtility.getCProject(input); |
| if (project != null) { |
| stores.add(new EclipsePreferencesAdapter(new ProjectScope(project.getProject()), CCorePlugin.PLUGIN_ID)); |
| } |
| |
| stores.add(CUIPlugin.getDefault().getPreferenceStore()); |
| stores.add(CUIPlugin.getDefault().getCorePreferenceStore()); |
| stores.add(EditorsUI.getPreferenceStore()); |
| |
| return new ChainedPreferenceStore(stores.toArray(new IPreferenceStore[stores.size()])); |
| } |
| |
| /** |
| * @return {@code true} if parser based Content Assist proposals are disabled. |
| * |
| * @since 5.0 |
| */ |
| public boolean isParserBasedContentAssistDisabled() { |
| return getPreferenceStore().getBoolean(PreferenceConstants.SCALABILITY_PARSER_BASED_CONTENT_ASSIST); |
| } |
| |
| /** |
| * @return {@code true} if Content Assist auto activation is disabled. |
| * |
| * @since 5.0 |
| */ |
| public boolean isContentAssistAutoActivartionDisabled() { |
| return getPreferenceStore().getBoolean(PreferenceConstants.SCALABILITY_CONTENT_ASSIST_AUTO_ACTIVATION); |
| } |
| |
| /** |
| * @return {@code true} if the number of lines in the file exceed |
| * the line number for scalability mode in the preference. |
| * |
| * @since 5.0 |
| */ |
| public boolean isEnableScalablilityMode() { |
| return fEnableScalablilityMode; |
| } |
| |
| @Override |
| protected boolean isPrefQuickDiffAlwaysOn() { |
| // Enable only if not in scalability mode. |
| // Workaround for http://bugs.eclipse.org/75555 |
| return super.isPrefQuickDiffAlwaysOn() && !isEnableScalablilityMode(); |
| } |
| |
| public boolean shouldProcessLocalParsingCompletions() { |
| return true; |
| } |
| |
| protected void uninstallOverrideIndicator() { |
| if (fOverrideIndicatorManager != null) { |
| fOverrideIndicatorManager.removeAnnotations(); |
| removeReconcileListener(fOverrideIndicatorManager); |
| fOverrideIndicatorManager= null; |
| } |
| } |
| |
| /** |
| * Determines whether the preference change encoded by the given event |
| * changes the override indication. |
| * |
| * @param event the event to be investigated |
| * @return {@code true} if event causes a change |
| * @since 5.3 |
| */ |
| 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()); |
| } |
| |
| /** |
| * Returns the boolean preference for the given key. |
| * |
| * @param store the preference store |
| * @param key the preference key |
| * @return {@code true} if the key exists in the store and its value is {@code true} |
| * @since 5.3 |
| */ |
| private boolean getBoolean(IPreferenceStore store, String key) { |
| return key != null && store.getBoolean(key); |
| } |
| |
| /** |
| * Tells whether override indicators are shown. |
| * |
| * @return {@code true} if the override indicators are shown |
| * @since 5.3 |
| */ |
| 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()); |
| } |
| |
| protected void installOverrideIndicator(boolean provideAST) { |
| uninstallOverrideIndicator(); |
| IAnnotationModel model= getDocumentProvider().getAnnotationModel(getEditorInput()); |
| |
| if (model == null) |
| return; |
| |
| fOverrideIndicatorManager= new OverrideIndicatorManager(model, null); |
| |
| addReconcileListener(fOverrideIndicatorManager); |
| |
| ICElement inputCElement = getInputCElement(); |
| if (provideAST && inputCElement instanceof ITranslationUnit) { |
| ASTProvider.getASTProvider().runOnAST(inputCElement, ASTProvider.WAIT_ACTIVE_ONLY, getProgressMonitor(), new ASTRunnable() { |
| @Override |
| public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) throws CoreException { |
| if (ast != null) |
| fOverrideIndicatorManager.reconciled(ast, true, getProgressMonitor()); |
| return Status.OK_STATUS; |
| }}); |
| } |
| } |
| |
| @Override |
| protected void editorSaved() { |
| super.editorSaved(); |
| IWorkingCopy inputCElement = getInputCElement(); |
| if (inputCElement != null) { |
| ITranslationUnit translationUnit = inputCElement.getOriginalElement(); |
| if (translationUnit != null) { |
| for (Object listener : fPostSaveListeners.getListeners()) { |
| ((IPostSaveListener) listener).saved(translationUnit, getProgressMonitor()); |
| } |
| } |
| } |
| } |
| |
| /** |
| * @since 5.4 |
| */ |
| public void addPostSaveListener(IPostSaveListener listener) { |
| fPostSaveListeners.add(listener); |
| } |
| |
| /** |
| * @since 5.4 |
| */ |
| public void removePostSaveListener(IPostSaveListener listener) { |
| fPostSaveListeners.remove(listener); |
| } |
| |
| @Override |
| protected IVerticalRulerColumn createAnnotationRulerColumn(CompositeRuler ruler) { |
| if (!getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_ANNOTATION_ROLL_OVER)) { |
| return super.createAnnotationRulerColumn(ruler); |
| } |
| |
| AnnotationRulerColumn column= new AnnotationRulerColumn(VERTICAL_RULER_WIDTH, getAnnotationAccess()); |
| column.setHover(new CExpandHover(ruler, getAnnotationAccess(), new IDoubleClickListener() { |
| |
| @Override |
| 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; |
| } |
| } |