| /******************************************************************************* |
| * Copyright (c) 2005, 2017 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: |
| * xored software, Inc. - initial API and implementation |
| * xored software, Inc. - fix tab handling (Bug# 200024) (Alex Panchenko) |
| *******************************************************************************/ |
| package org.eclipse.dltk.internal.ui.editor; |
| |
| import java.text.CharacterIterator; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Stack; |
| |
| import org.eclipse.core.filebuffers.IPersistableAnnotationModel; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.ProjectScope; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.ListenerList; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.core.runtime.preferences.DefaultScope; |
| import org.eclipse.core.runtime.preferences.InstanceScope; |
| import org.eclipse.dltk.compiler.CharOperation; |
| import org.eclipse.dltk.core.DLTKCore; |
| import org.eclipse.dltk.core.DLTKLanguageManager; |
| import org.eclipse.dltk.core.IDLTKLanguageToolkit; |
| import org.eclipse.dltk.core.IImportDeclaration; |
| import org.eclipse.dltk.core.ILocalVariable; |
| import org.eclipse.dltk.core.IMember; |
| import org.eclipse.dltk.core.IModelElement; |
| import org.eclipse.dltk.core.IPackageDeclaration; |
| import org.eclipse.dltk.core.IScriptLanguageProvider; |
| import org.eclipse.dltk.core.IScriptProject; |
| import org.eclipse.dltk.core.ISourceModule; |
| import org.eclipse.dltk.core.ISourceRange; |
| import org.eclipse.dltk.core.ISourceReference; |
| import org.eclipse.dltk.core.ModelException; |
| import org.eclipse.dltk.core.PreferencesLookupDelegate; |
| import org.eclipse.dltk.core.ScriptModelUtil; |
| import org.eclipse.dltk.internal.ui.BrowserInformationControl; |
| import org.eclipse.dltk.internal.ui.actions.CompositeActionGroup; |
| import org.eclipse.dltk.internal.ui.actions.FoldingActionGroup; |
| import org.eclipse.dltk.internal.ui.actions.refactoring.RefactorActionGroup; |
| import org.eclipse.dltk.internal.ui.editor.SourceModuleDocumentProvider.SourceModuleAnnotationModel; |
| import org.eclipse.dltk.internal.ui.editor.selectionaction.GoToNextPreviousMemberAction; |
| import org.eclipse.dltk.internal.ui.editor.semantic.highlighting.SemanticHighlightingManager; |
| import org.eclipse.dltk.internal.ui.editor.semantic.highlighting.SemanticHighlightingReconciler; |
| import org.eclipse.dltk.internal.ui.text.DLTKWordIterator; |
| import org.eclipse.dltk.internal.ui.text.DocumentCharacterIterator; |
| import org.eclipse.dltk.internal.ui.text.HTMLTextPresenter; |
| import org.eclipse.dltk.internal.ui.text.IScriptReconcilingListener; |
| import org.eclipse.dltk.internal.ui.text.hover.ScriptExpandHover; |
| import org.eclipse.dltk.internal.ui.text.hover.SourceViewerInformationControl; |
| import org.eclipse.dltk.ui.CodeFormatterConstants; |
| import org.eclipse.dltk.ui.DLTKUILanguageManager; |
| import org.eclipse.dltk.ui.DLTKUIPlugin; |
| import org.eclipse.dltk.ui.EclipsePreferencesAdapter; |
| import org.eclipse.dltk.ui.IContextMenuConstants; |
| import org.eclipse.dltk.ui.IDLTKUILanguageToolkit; |
| import org.eclipse.dltk.ui.IWorkingCopyManager; |
| import org.eclipse.dltk.ui.PreferenceConstants; |
| import org.eclipse.dltk.ui.PreferencesAdapter; |
| import org.eclipse.dltk.ui.actions.DLTKActionConstants; |
| import org.eclipse.dltk.ui.actions.GenerateActionGroup; |
| import org.eclipse.dltk.ui.actions.IScriptEditorActionDefinitionIds; |
| import org.eclipse.dltk.ui.actions.OpenEditorActionGroup; |
| import org.eclipse.dltk.ui.actions.OpenViewActionGroup; |
| import org.eclipse.dltk.ui.actions.SearchActionGroup; |
| import org.eclipse.dltk.ui.editor.IScriptAnnotation; |
| import org.eclipse.dltk.ui.editor.highlighting.ISemanticHighlightingUpdater; |
| import org.eclipse.dltk.ui.formatter.IScriptFormatterFactory; |
| import org.eclipse.dltk.ui.formatter.ScriptFormatterManager; |
| import org.eclipse.dltk.ui.formatter.internal.ScriptFormattingContextProperties; |
| import org.eclipse.dltk.ui.text.ScriptSourceViewerConfiguration; |
| import org.eclipse.dltk.ui.text.ScriptTextTools; |
| import org.eclipse.dltk.ui.text.folding.FoldingProviderManager; |
| import org.eclipse.dltk.ui.text.folding.IFoldingStructureProvider; |
| import org.eclipse.dltk.ui.text.folding.IFoldingStructureProviderExtension; |
| import org.eclipse.dltk.ui.text.templates.ITemplateAccess; |
| import org.eclipse.jface.action.Action; |
| import org.eclipse.jface.action.GroupMarker; |
| import org.eclipse.jface.action.IAction; |
| import org.eclipse.jface.action.IMenuManager; |
| import org.eclipse.jface.action.MenuManager; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.jface.resource.JFaceResources; |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.BadPositionCategoryException; |
| import org.eclipse.jface.text.DefaultInformationControl; |
| import org.eclipse.jface.text.DocumentCommand; |
| import org.eclipse.jface.text.DocumentEvent; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.IInformationControlCreator; |
| import org.eclipse.jface.text.IPositionUpdater; |
| import org.eclipse.jface.text.IRegion; |
| import org.eclipse.jface.text.ITextHover; |
| import org.eclipse.jface.text.ITextOperationTarget; |
| import org.eclipse.jface.text.ITextSelection; |
| import org.eclipse.jface.text.ITextViewerExtension2; |
| import org.eclipse.jface.text.ITextViewerExtension5; |
| import org.eclipse.jface.text.IWidgetTokenKeeper; |
| import org.eclipse.jface.text.Position; |
| import org.eclipse.jface.text.Region; |
| import org.eclipse.jface.text.TextSelection; |
| import org.eclipse.jface.text.contentassist.ContentAssistEvent; |
| import org.eclipse.jface.text.contentassist.ContentAssistant; |
| import org.eclipse.jface.text.contentassist.ICompletionListener; |
| import org.eclipse.jface.text.contentassist.ICompletionProposal; |
| import org.eclipse.jface.text.contentassist.IContentAssistant; |
| import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; |
| import org.eclipse.jface.text.formatter.FormattingContextProperties; |
| import org.eclipse.jface.text.formatter.IFormattingContext; |
| import org.eclipse.jface.text.information.InformationPresenter; |
| 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.reconciler.IReconciler; |
| 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.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.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.IPostSelectionProvider; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.ISelectionProvider; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.ST; |
| import org.eclipse.swt.custom.StyledText; |
| 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.IEditorInput; |
| import org.eclipse.ui.IPageLayout; |
| import org.eclipse.ui.IPartListener2; |
| import org.eclipse.ui.IPartService; |
| import org.eclipse.ui.ISelectionListener; |
| import org.eclipse.ui.IWorkbenchPage; |
| import org.eclipse.ui.IWorkbenchPart; |
| import org.eclipse.ui.IWorkbenchPartReference; |
| import org.eclipse.ui.IWorkbenchPartSite; |
| import org.eclipse.ui.IWorkbenchWindow; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.actions.ActionContext; |
| import org.eclipse.ui.actions.ActionGroup; |
| import org.eclipse.ui.editors.text.EditorsUI; |
| import org.eclipse.ui.part.IShowInTargetList; |
| import org.eclipse.ui.texteditor.AbstractDecoratedTextEditor; |
| import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants; |
| 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.SourceViewerDecorationSupport; |
| import org.eclipse.ui.texteditor.TextNavigationAction; |
| import org.eclipse.ui.texteditor.TextOperationAction; |
| import org.eclipse.ui.texteditor.templates.ITemplatesPage; |
| import org.eclipse.ui.views.contentoutline.ContentOutline; |
| import org.eclipse.ui.views.contentoutline.IContentOutlinePage; |
| |
| import com.ibm.icu.text.BreakIterator; |
| |
| public abstract class ScriptEditor extends AbstractDecoratedTextEditor |
| implements IScriptReconcilingListener, IScriptLanguageProvider, |
| IScriptEditor { |
| |
| /** The editor's save policy */ |
| protected ISavePolicy fSavePolicy = null; |
| |
| /** Preference key for matching brackets */ |
| protected final static String MATCHING_BRACKETS = PreferenceConstants.EDITOR_MATCHING_BRACKETS; |
| /** Preference key for matching brackets color */ |
| protected final static String MATCHING_BRACKETS_COLOR = PreferenceConstants.EDITOR_MATCHING_BRACKETS_COLOR; |
| |
| private ScriptEditorErrorTickUpdater fScriptEditorErrorTickUpdater; |
| |
| private OccurrencesFinder occurrencesFinder; |
| |
| private static String[] GLOBAL_FOLDING_PROPERTIES = { |
| PreferenceConstants.EDITOR_FOLDING_ENABLED, |
| PreferenceConstants.EDITOR_COMMENTS_FOLDING_ENABLED, |
| PreferenceConstants.EDITOR_FOLDING_LINES_LIMIT, |
| PreferenceConstants.EDITOR_COMMENT_FOLDING_JOIN_NEWLINES }; |
| |
| public ISourceViewer getScriptSourceViewer() { |
| return super.getSourceViewer(); |
| } |
| |
| public static class BracketLevel { |
| public int fOffset; |
| public int fLength; |
| public LinkedModeUI fUI; |
| public Position fFirstPosition; |
| public Position fSecondPosition; |
| } |
| |
| public class ExitPolicy implements IExitPolicy { |
| |
| public final char fExitCharacter; |
| public final char fEscapeCharacter; |
| public final Stack fStack; |
| public final int fSize; |
| |
| public ExitPolicy(char exitCharacter, char escapeCharacter, |
| Stack stack) { |
| fExitCharacter = exitCharacter; |
| fEscapeCharacter = escapeCharacter; |
| fStack = stack; |
| fSize = fStack.size(); |
| } |
| |
| /* |
| * @see |
| * org.eclipse.jdt.internal.ui.text.link.LinkedPositionUI.ExitPolicy |
| * #doExit(org.eclipse.jdt.internal.ui.text.link.LinkedPositionManager, |
| * org.eclipse.swt.events.VerifyEvent, int, int) |
| */ |
| @Override |
| public ExitFlags doExit(LinkedModeModel model, VerifyEvent event, |
| int offset, int length) { |
| |
| if (fSize == fStack.size() && !isMasked(offset)) { |
| if (event.character == fExitCharacter) { |
| BracketLevel level = (BracketLevel) 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) { |
| // ssanders: If completion popup is displayed, Enter |
| // dismisses it |
| if (((AdaptedSourceViewer) getScriptSourceViewer()).fInCompletionSession) |
| return new ExitFlags(ILinkedModeListener.NONE, true); |
| |
| IDocument document = getSourceViewer().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 = getSourceViewer().getDocument(); |
| try { |
| return fEscapeCharacter == document.getChar(offset - 1); |
| } catch (BadLocationException e) { |
| } |
| return false; |
| } |
| } |
| |
| static class ExclusivePositionUpdater implements IPositionUpdater { |
| |
| /** The position category. */ |
| private final String fCategory; |
| |
| /** |
| * Creates a new updater for the given <code>category</code>. |
| * |
| * @param category |
| * the new category. |
| */ |
| public ExclusivePositionUpdater(String category) { |
| fCategory = category; |
| } |
| |
| /* |
| * @see |
| * org.eclipse.jface.text.IPositionUpdater#update(org.eclipse.jface. |
| * text.DocumentEvent) |
| */ |
| @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; |
| } |
| |
| } |
| |
| /** |
| * Text operation code for requesting common prefix completion. |
| */ |
| public static final int CONTENTASSIST_COMPLETE_PREFIX = 60; |
| |
| interface ITextConverter { |
| void customizeDocumentCommand(IDocument document, |
| DocumentCommand command); |
| } |
| |
| class AdaptedSourceViewer extends ScriptSourceViewer |
| implements ICompletionListener { |
| private List<ITextConverter> fTextConverters; |
| |
| private boolean fIgnoreTextConverters = false; |
| private boolean fInCompletionSession; |
| |
| protected IContentAssistant getContentAssistant() { |
| return fContentAssistant; |
| } |
| |
| public AdaptedSourceViewer(Composite parent, |
| IVerticalRuler verticalRuler, IOverviewRuler overviewRuler, |
| boolean showAnnotationsOverview, int styles, |
| IPreferenceStore store) { |
| super(parent, verticalRuler, overviewRuler, showAnnotationsOverview, |
| styles, store); |
| } |
| |
| @Override |
| public void configure(SourceViewerConfiguration configuration) { |
| super.configure(configuration); |
| |
| final IContentAssistant ca = getContentAssistant(); |
| if (ca instanceof IContentAssistantExtension2) { |
| ((IContentAssistantExtension2) ca).addCompletionListener(this); |
| } |
| } |
| |
| @Override |
| public void unconfigure() { |
| final IContentAssistant ca = getContentAssistant(); |
| if (ca instanceof IContentAssistantExtension2) { |
| ((IContentAssistantExtension2) ca) |
| .removeCompletionListener(this); |
| } |
| |
| super.unconfigure(); |
| } |
| |
| /* |
| * @see ITextOperationTarget#doOperation(int) |
| */ |
| @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: |
| /* |
| * XXX: We can get rid of this once the SourceViewer has a way |
| * to update the status line |
| * https://bugs.eclipse.org/bugs/show_bug.cgi?id=133787 |
| */ |
| msg = fQuickAssistAssistant.showPossibleQuickAssists(); |
| setStatusLineErrorMessage(msg); |
| return; |
| case UNDO: |
| fIgnoreTextConverters = true; |
| super.doOperation(operation); |
| fIgnoreTextConverters = false; |
| return; |
| case REDO: |
| fIgnoreTextConverters = true; |
| super.doOperation(operation); |
| fIgnoreTextConverters = false; |
| return; |
| } |
| |
| super.doOperation(operation); |
| } |
| |
| public void insertTextConverter(ITextConverter textConverter, |
| int index) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public void addTextConverter(ITextConverter textConverter) { |
| if (fTextConverters == null) { |
| fTextConverters = new ArrayList<ITextConverter>(1); |
| fTextConverters.add(textConverter); |
| } else if (!fTextConverters.contains(textConverter)) |
| fTextConverters.add(textConverter); |
| } |
| |
| public void removeTextConverter(ITextConverter textConverter) { |
| if (fTextConverters != null) { |
| fTextConverters.remove(textConverter); |
| if (fTextConverters.size() == 0) |
| fTextConverters = null; |
| } |
| } |
| |
| /* |
| * @see TextViewer#customizeDocumentCommand(DocumentCommand) |
| */ |
| @Override |
| protected void customizeDocumentCommand(DocumentCommand command) { |
| super.customizeDocumentCommand(command); |
| if (!fIgnoreTextConverters && fTextConverters != null) { |
| for (ITextConverter c : fTextConverters) |
| c.customizeDocumentCommand(getDocument(), command); |
| } |
| } |
| |
| @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); |
| } |
| |
| @Override |
| public void assistSessionEnded(ContentAssistEvent event) { |
| fInCompletionSession = false; |
| } |
| |
| @Override |
| public void assistSessionStarted(ContentAssistEvent event) { |
| fInCompletionSession = true; |
| } |
| |
| @Override |
| public void selectionChanged(ICompletionProposal proposal, |
| boolean smartToggle) { |
| } |
| |
| private IProject getProject() { |
| final IModelElement input = getInputModelElement(); |
| if (input != null) { |
| final IScriptProject scriptProject = input.getScriptProject(); |
| if (scriptProject != null) { |
| return scriptProject.getProject(); |
| } |
| } |
| return null; |
| } |
| |
| private ISourceModule getSourceModule() { |
| final IModelElement input = getInputModelElement(); |
| if (input != null) { |
| return (ISourceModule) input |
| .getAncestor(IModelElement.SOURCE_MODULE); |
| } |
| return null; |
| } |
| |
| @Override |
| public IFormattingContext createFormattingContext() { |
| final IFormattingContext context = super.createFormattingContext(); |
| context.setProperty(ScriptFormattingContextProperties.MODULE, |
| getSourceModule()); |
| final IProject project = getProject(); |
| context.setProperty( |
| ScriptFormattingContextProperties.CONTEXT_PROJECT, project); |
| final IScriptFormatterFactory factory = ScriptFormatterManager |
| .getSelected(getLanguageToolkit().getNatureId(), project); |
| if (factory != null) { |
| context.setProperty( |
| ScriptFormattingContextProperties.CONTEXT_FORMATTER_ID, |
| factory.getId()); |
| final Map<String, String> preferences = factory |
| .retrievePreferences( |
| new PreferencesLookupDelegate(project)); |
| context.setProperty( |
| FormattingContextProperties.CONTEXT_PREFERENCES, |
| preferences); |
| } |
| return context; |
| } |
| } |
| |
| /** |
| * Internal implementation class for a change listener. |
| * |
| * |
| */ |
| protected abstract class AbstractSelectionChangedListener |
| implements ISelectionChangedListener { |
| /** |
| * Installs this selection changed listener with the given selection |
| * provider. If the selection provider is a post selection provider, |
| * post selection changed events are the preferred choice, otherwise |
| * normal selection changed events are requested. |
| * |
| * @param selectionProvider |
| */ |
| public void install(ISelectionProvider selectionProvider) { |
| if (selectionProvider == null) |
| return; |
| if (selectionProvider instanceof IPostSelectionProvider) { |
| IPostSelectionProvider provider = (IPostSelectionProvider) selectionProvider; |
| provider.addPostSelectionChangedListener(this); |
| } else { |
| selectionProvider.addSelectionChangedListener(this); |
| } |
| } |
| |
| /** |
| * Removes this selection changed listener from the given selection |
| * provider. |
| * |
| * @param selectionProvider |
| * the selection provider |
| */ |
| public void uninstall(ISelectionProvider selectionProvider) { |
| if (selectionProvider == null) |
| return; |
| if (selectionProvider instanceof IPostSelectionProvider) { |
| IPostSelectionProvider provider = (IPostSelectionProvider) selectionProvider; |
| provider.removePostSelectionChangedListener(this); |
| } else { |
| selectionProvider.removeSelectionChangedListener(this); |
| } |
| } |
| } |
| |
| /** |
| * Updates the selection in the editor's widget with the selection of the |
| * outline page. |
| */ |
| class OutlineSelectionChangedListener |
| extends AbstractSelectionChangedListener { |
| @Override |
| public void selectionChanged(SelectionChangedEvent event) { |
| doSelectionChanged(event); |
| } |
| } |
| |
| private ScriptOutlinePage fOutlinePage; |
| |
| private ProjectionSupport fProjectionSupport; |
| |
| /** |
| * This editor's projection model updater |
| */ |
| private IFoldingStructureProvider fProjectionModelUpdater; |
| |
| /** |
| * The action group for folding. |
| */ |
| private ActionGroup fFoldingGroup; |
| |
| /** The information presenter. */ |
| private InformationPresenter fInformationPresenter; |
| |
| private CompositeActionGroup fContextMenuGroup; |
| |
| // private SelectionHistory fSelectionHistory; |
| |
| private CompositeActionGroup fActionGroups; |
| |
| private AbstractSelectionChangedListener fOutlineSelectionChangedListener = new OutlineSelectionChangedListener(); |
| |
| /** |
| * Updates the script outline page selection and this editor's range |
| * indicator. |
| * |
| * |
| */ |
| private class EditorSelectionChangedListener |
| extends AbstractSelectionChangedListener { |
| /* |
| * @see |
| * org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged |
| * (org.eclipse.jface.viewers.SelectionChangedEvent) |
| */ |
| @Override |
| public void selectionChanged(SelectionChangedEvent event) { |
| // XXX: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=56161 |
| ScriptEditor.this.selectionChanged(); |
| } |
| } |
| |
| /** |
| * The editor selection changed listener. |
| */ |
| private EditorSelectionChangedListener fEditorSelectionChangedListener; |
| |
| public ScriptEditor() { |
| super(); |
| setDocumentProvider( |
| DLTKUIPlugin.getDefault().getSourceModuleDocumentProvider()); |
| fScriptEditorErrorTickUpdater = new ScriptEditorErrorTickUpdater(this); |
| } |
| |
| /** |
| * @see org.eclipse.ui.texteditor.StatusTextEditor#handleElementContentReplaced() |
| */ |
| @Override |
| protected void handleElementContentReplaced() { |
| super.handleElementContentReplaced(); |
| |
| IAnnotationModel annotationModel = getScriptSourceViewer() |
| .getAnnotationModel(); |
| if (annotationModel instanceof IPersistableAnnotationModel) { |
| try { |
| ((IPersistableAnnotationModel) annotationModel) |
| .reinitialize(getScriptSourceViewer().getDocument()); |
| } catch (CoreException ex) { |
| ex.printStackTrace(); |
| } |
| } |
| } |
| |
| @Override |
| public void dispose() { |
| |
| if (fProjectionModelUpdater != null) { |
| fProjectionModelUpdater.uninstall(); |
| fProjectionModelUpdater = null; |
| } |
| if (occurrencesFinder != null) { |
| occurrencesFinder.dispose(); |
| occurrencesFinder = null; |
| } |
| |
| // ISourceViewer sourceViewer= getSourceViewer(); |
| // if (sourceViewer instanceof ITextViewerExtension) |
| // ((ITextViewerExtension) |
| // sourceViewer).removeVerifyKeyListener(fBracketInserter); |
| |
| if (fScriptEditorErrorTickUpdater != null) { |
| fScriptEditorErrorTickUpdater.dispose(); |
| fScriptEditorErrorTickUpdater = null; |
| } |
| |
| // if (fCorrectionCommands != null) { |
| // fCorrectionCommands.deregisterCommands(); |
| // fCorrectionCommands= null; |
| // } |
| uninstallSemanticHighlighting(); |
| super.dispose(); |
| } |
| |
| @Override |
| protected void initializeEditor() { |
| occurrencesFinder = new OccurrencesFinder(this); |
| if (!occurrencesFinder.isValid()) { |
| occurrencesFinder = null; |
| } |
| IPreferenceStore store = createCombinedPreferenceStore(null); |
| setPreferenceStore(store); |
| ScriptTextTools textTools = getTextTools(); |
| if (textTools != null) { |
| setSourceViewerConfiguration( |
| textTools.createSourceViewerConfiguraton(store, this)); |
| } |
| } |
| |
| /** |
| * 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) { |
| final List<IPreferenceStore> stores = new ArrayList<IPreferenceStore>( |
| 8); |
| final IScriptProject project = EditorUtility.getScriptProject(input); |
| final IDLTKLanguageToolkit toolkit = getLanguageToolkit(); |
| final String preferenceQualifier = toolkit.getPreferenceQualifier(); |
| if (project != null) { |
| if (preferenceQualifier != null) { |
| stores.add(new EclipsePreferencesAdapter( |
| new ProjectScope(project.getProject()), |
| preferenceQualifier)); |
| } |
| stores.add(new EclipsePreferencesAdapter( |
| new ProjectScope(project.getProject()), |
| DLTKCore.PLUGIN_ID)); |
| } |
| stores.add(getScriptPreferenceStore()); |
| if (preferenceQualifier != null) { |
| stores.add(new EclipsePreferencesAdapter(InstanceScope.INSTANCE, |
| preferenceQualifier)); |
| stores.add(new EclipsePreferencesAdapter(DefaultScope.INSTANCE, |
| preferenceQualifier)); |
| } |
| stores.add(new PreferencesAdapter( |
| DLTKCore.getDefault().getPluginPreferences())); |
| stores.add(EditorsUI.getPreferenceStore()); |
| stores.add(PlatformUI.getPreferenceStore()); |
| return new ChainedPreferenceStore( |
| stores.toArray(new IPreferenceStore[stores.size()])); |
| } |
| |
| public IPreferenceStore getScriptPreferenceStore() { |
| IDLTKLanguageToolkit toolkit = getLanguageToolkit(); |
| if (toolkit != null) { |
| IDLTKUILanguageToolkit uiToolkit = DLTKUILanguageManager |
| .getLanguageToolkit(toolkit.getNatureId()); |
| if (uiToolkit != null) { |
| return uiToolkit.getPreferenceStore(); |
| } |
| } |
| return null; |
| } |
| |
| public ScriptTextTools getTextTools() { |
| return null; |
| } |
| |
| protected void connectPartitioningToElement(IEditorInput input, |
| IDocument document) { |
| } |
| |
| protected void internalDoSetInput(IEditorInput input) throws CoreException { |
| ISourceViewer sourceViewer = getSourceViewer(); |
| ScriptSourceViewer scriptSourceViewer = null; |
| if (sourceViewer instanceof ScriptSourceViewer) |
| scriptSourceViewer = (ScriptSourceViewer) sourceViewer; |
| IPreferenceStore store = getPreferenceStore(); |
| |
| if (scriptSourceViewer != null && isFoldingEnabled() && (store == null |
| || !store.getBoolean(PreferenceConstants.EDITOR_SHOW_SEGMENTS))) |
| scriptSourceViewer.prepareDelayedProjection(); |
| |
| // correct connection code here. |
| |
| super.doSetInput(input); |
| |
| final IDocumentProvider docProvider = getDocumentProvider(); |
| final IAnnotationModel model = docProvider.getAnnotationModel(input); |
| if (model instanceof SourceModuleAnnotationModel) { |
| ((SourceModuleAnnotationModel) model).problemFactory = DLTKLanguageManager |
| .getProblemFactory(getNatureId()); |
| } |
| final IDocument doc = docProvider.getDocument(input); |
| connectPartitioningToElement(input, doc); |
| |
| if (scriptSourceViewer != null |
| && scriptSourceViewer.getReconciler() == null) { |
| IReconciler reconciler = getSourceViewerConfiguration() |
| .getReconciler(scriptSourceViewer); |
| if (reconciler != null) { |
| reconciler.install(scriptSourceViewer); |
| scriptSourceViewer.setReconciler(reconciler); |
| } |
| } |
| if (DLTKCore.DEBUG) { |
| System.err.println( |
| "TODO: Add encoding support and overriding indicator support"); //$NON-NLS-1$ |
| } |
| // if (fEncodingSupport != null) |
| // fEncodingSupport.reset(); |
| // if (isShowingOverrideIndicators()) |
| // installOverrideIndicator(false); |
| setOutlinePageInput(fOutlinePage, input); |
| } |
| |
| private boolean isFoldingEnabled() { |
| return getPreferenceStore() |
| .getBoolean(PreferenceConstants.EDITOR_FOLDING_ENABLED); |
| } |
| |
| @Override |
| public boolean isSaveAsAllowed() { |
| return true; |
| } |
| |
| /** |
| * Returns the standard action group of this editor. |
| * |
| * @return returns this editor's standard action group |
| */ |
| ActionGroup getActionGroup() { |
| return fActionGroups; |
| } |
| |
| /* |
| * @see AbstractTextEditor#editorContextMenuAboutToShow |
| */ |
| @Override |
| public void editorContextMenuAboutToShow(IMenuManager menu) { |
| super.editorContextMenuAboutToShow(menu); |
| |
| menu.insertAfter(IContextMenuConstants.GROUP_OPEN, |
| new GroupMarker(IContextMenuConstants.GROUP_SHOW)); |
| |
| ActionContext context = new ActionContext( |
| getSelectionProvider().getSelection()); |
| context.setInput(getEditorInput()); |
| fContextMenuGroup.setContext(context); |
| fContextMenuGroup.fillContextMenu(menu); |
| fContextMenuGroup.setContext(null); |
| // Quick views |
| menu.appendToGroup(IContextMenuConstants.GROUP_OPEN, |
| getAction(IScriptEditorActionDefinitionIds.SHOW_OUTLINE)); |
| menu.appendToGroup(IContextMenuConstants.GROUP_OPEN, |
| getAction(IScriptEditorActionDefinitionIds.OPEN_HIERARCHY)); |
| } |
| |
| @Override |
| protected void createActions() { |
| super.createActions(); |
| |
| ActionGroup oeg = new OpenEditorActionGroup(this); |
| ActionGroup ovg = new OpenViewActionGroup(this); |
| ActionGroup dsg = new SearchActionGroup(this); |
| |
| fActionGroups = new CompositeActionGroup( |
| new ActionGroup[] { oeg, ovg, dsg }); |
| |
| // fSelectionHistory= new SelectionHistory(this); |
| |
| fContextMenuGroup = new CompositeActionGroup( |
| new ActionGroup[] { oeg, ovg, dsg }); |
| loadContributedContextActionGroups(); |
| |
| fFoldingGroup = createFoldingActionGroup(); |
| |
| // ResourceAction resAction = new TextOperationAction(DLTKEditorMessages |
| // .getBundleForConstructedKeys(), "ShowDocumentaion.", this, |
| // ISourceViewer.INFORMATION, true); |
| // |
| // resAction = new InformationDispatchAction(DLTKEditorMessages |
| // .getBundleForConstructedKeys(), "ShowDocumentation.", |
| // (TextOperationAction) resAction); |
| // |
| // resAction |
| // .setActionDefinitionId(IScriptEditorActionDefinitionIds. |
| // SHOW_DOCUMENTATION); |
| // setAction("ShowDocumentation", resAction); |
| |
| Action action = new GotoMatchingBracketAction(this); |
| action.setActionDefinitionId( |
| IScriptEditorActionDefinitionIds.GOTO_MATCHING_BRACKET); |
| setAction(GotoMatchingBracketAction.GOTO_MATCHING_BRACKET, action); |
| |
| Action outlineAction = new TextOperationAction( |
| DLTKEditorMessages.getBundleForConstructedKeys(), |
| "ShowOutline.", this, //$NON-NLS-1$ |
| ScriptSourceViewer.SHOW_OUTLINE, true); |
| outlineAction.setActionDefinitionId( |
| IScriptEditorActionDefinitionIds.SHOW_OUTLINE); |
| setAction(IScriptEditorActionDefinitionIds.SHOW_OUTLINE, outlineAction); |
| |
| action = new TextOperationAction( |
| DLTKEditorMessages.getBundleForConstructedKeys(), |
| "OpenHierarchy.", this, ScriptSourceViewer.SHOW_HIERARCHY, //$NON-NLS-1$ |
| true); |
| action.setActionDefinitionId( |
| IScriptEditorActionDefinitionIds.OPEN_HIERARCHY); |
| setAction(IScriptEditorActionDefinitionIds.OPEN_HIERARCHY, action); |
| |
| // ContentAssistProposal |
| action = new ContentAssistAction( |
| DLTKEditorMessages.getBundleForConstructedKeys(), |
| "ContentAssistProposal.", this); //$NON-NLS-1$ |
| action.setActionDefinitionId( |
| ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS); |
| setAction("ContentAssistProposal", action); //$NON-NLS-1$ |
| markAsStateDependentAction("ContentAssistProposal", true); //$NON-NLS-1$ |
| |
| // ContentAssistContextInformation |
| action = new TextOperationAction( |
| DLTKEditorMessages.getBundleForConstructedKeys(), |
| "ContentAssistContextInformation.", this, //$NON-NLS-1$ |
| ISourceViewer.CONTENTASSIST_CONTEXT_INFORMATION); |
| action.setActionDefinitionId( |
| ITextEditorActionDefinitionIds.CONTENT_ASSIST_CONTEXT_INFORMATION); |
| setAction("ContentAssistContextInformation", action); //$NON-NLS-1$ |
| markAsStateDependentAction("ContentAssistContextInformation", true); //$NON-NLS-1$ |
| |
| // GroupEdit |
| ActionGroup rg = new RefactorActionGroup(this, |
| ITextEditorActionConstants.GROUP_EDIT); |
| fActionGroups.addGroup(rg); |
| fContextMenuGroup.addGroup(rg); |
| |
| // GoToNextMember |
| action = GoToNextPreviousMemberAction.newGoToNextMemberAction(this); |
| action.setActionDefinitionId( |
| IScriptEditorActionDefinitionIds.GOTO_NEXT_MEMBER); |
| setAction(GoToNextPreviousMemberAction.NEXT_MEMBER, action); |
| |
| // GoToPreviousMember |
| action = GoToNextPreviousMemberAction.newGoToPreviousMemberAction(this); |
| action.setActionDefinitionId( |
| IScriptEditorActionDefinitionIds.GOTO_PREVIOUS_MEMBER); |
| setAction(GoToNextPreviousMemberAction.PREVIOUS_MEMBER, action); |
| |
| // Source menu actions |
| action = new TextOperationAction( |
| DLTKEditorMessages.getBundleForConstructedKeys(), "Comment.", //$NON-NLS-1$ |
| this, ITextOperationTarget.PREFIX); |
| action.setActionDefinitionId(IScriptEditorActionDefinitionIds.COMMENT); |
| setAction(DLTKActionConstants.COMMENT, action); |
| markAsStateDependentAction(DLTKActionConstants.COMMENT, true); |
| |
| action = new TextOperationAction( |
| DLTKEditorMessages.getBundleForConstructedKeys(), "Uncomment.", //$NON-NLS-1$ |
| this, ITextOperationTarget.STRIP_PREFIX); |
| action.setActionDefinitionId( |
| IScriptEditorActionDefinitionIds.UNCOMMENT); |
| setAction(DLTKActionConstants.UNCOMMENT, action); |
| markAsStateDependentAction(DLTKActionConstants.UNCOMMENT, true); |
| |
| action = new ToggleCommentAction( |
| DLTKEditorMessages.getBundleForConstructedKeys(), |
| "ToggleComment.", this); //$NON-NLS-1$ |
| action.setActionDefinitionId( |
| IScriptEditorActionDefinitionIds.TOGGLE_COMMENT); |
| setAction(DLTKActionConstants.TOGGLE_COMMENT, action); |
| markAsStateDependentAction(DLTKActionConstants.TOGGLE_COMMENT, true); |
| |
| ISourceViewer sourceViewer = getSourceViewer(); |
| SourceViewerConfiguration configuration = getSourceViewerConfiguration(); |
| ((ToggleCommentAction) action).configure(sourceViewer, configuration); |
| |
| final ActionGroup generateActions = createGenerateActionGroup(); |
| if (generateActions != null) { |
| fActionGroups.addGroup(generateActions); |
| fContextMenuGroup.addGroup(generateActions); |
| } |
| } |
| |
| /** |
| * @since 3.0 |
| */ |
| protected ActionGroup createGenerateActionGroup() { |
| return new GenerateActionGroup(this, |
| ITextEditorActionConstants.GROUP_EDIT); |
| } |
| |
| private static final String EXTENSION_EDITOR_CONTEXT_ACTION_GROUPS = "editorContextActionGroup"; //$NON-NLS-1$ |
| private static final String ATTR_NATURE = "nature"; //$NON-NLS-1$ |
| private static final String ATTR_CLASS = "class"; //$NON-NLS-1$ |
| |
| private void loadContributedContextActionGroups() { |
| final IConfigurationElement[] elements = Platform.getExtensionRegistry() |
| .getConfigurationElementsFor(DLTKUIPlugin.PLUGIN_ID, |
| EXTENSION_EDITOR_CONTEXT_ACTION_GROUPS); |
| final String natureId = getNatureId(); |
| for (int i = 0; i < elements.length; ++i) { |
| final IConfigurationElement element = elements[i]; |
| final String elementNature = element.getAttribute(ATTR_NATURE); |
| if (elementNature == null || elementNature.equals(natureId)) { |
| try { |
| final Object actionGroup = element |
| .createExecutableExtension(ATTR_CLASS); |
| if (actionGroup instanceof ActionGroup) { |
| fContextMenuGroup.addGroup((ActionGroup) actionGroup); |
| } else { |
| DLTKUIPlugin.logErrorMessage( |
| actionGroup.getClass().getName() |
| + " should extend ActionGroup"); //$NON-NLS-1$ |
| } |
| } catch (CoreException e) { |
| DLTKUIPlugin.log(e); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Creates action group for folding. |
| * |
| * @return |
| * @since 3.0 |
| */ |
| protected ActionGroup createFoldingActionGroup() { |
| return new FoldingActionGroup(this, getViewer(), |
| getScriptPreferenceStore()); |
| } |
| |
| @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 ScriptExpandHover(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; |
| } |
| |
| /** |
| * Returns the folding action group, or <code>null</code> if there is none. |
| * |
| * @return the folding action group, or <code>null</code> if there is none |
| * |
| */ |
| ActionGroup getFoldingActionGroup() { |
| return fFoldingGroup; |
| } |
| |
| public final ISourceViewer getViewer() { |
| return getSourceViewer(); |
| } |
| |
| @Override |
| protected void doSetInput(IEditorInput input) throws CoreException { |
| ISourceViewer sourceViewer = getSourceViewer(); |
| if (!(sourceViewer instanceof ISourceViewerExtension2)) { |
| setPreferenceStore(createCombinedPreferenceStore(input)); |
| try { |
| internalDoSetInput(input); |
| } catch (ModelException e) { |
| DLTKUIPlugin.log(e); |
| this.close(false); |
| } |
| } else { |
| // uninstall & unregister preference store listener |
| getSourceViewerDecorationSupport(sourceViewer).uninstall(); |
| ((ISourceViewerExtension2) sourceViewer).unconfigure(); |
| setPreferenceStore(createCombinedPreferenceStore(input)); |
| // install & register preference store listener |
| sourceViewer.configure(getSourceViewerConfiguration()); |
| getSourceViewerDecorationSupport(sourceViewer) |
| .install(getPreferenceStore()); |
| try { |
| internalDoSetInput(input); |
| } catch (ModelException e) { |
| DLTKUIPlugin.log(e); |
| this.close(false); |
| } |
| } |
| |
| if (fScriptEditorErrorTickUpdater != null) |
| fScriptEditorErrorTickUpdater |
| .updateEditorImage(getInputModelElement()); |
| } |
| |
| @Override |
| protected void setPreferenceStore(IPreferenceStore store) { |
| super.setPreferenceStore(store); |
| final SourceViewerConfiguration svConfiguration = getSourceViewerConfiguration(); |
| if (svConfiguration == null |
| || svConfiguration instanceof ScriptSourceViewerConfiguration) { |
| final ScriptTextTools textTools = getTextTools(); |
| if (textTools != null) { |
| setSourceViewerConfiguration( |
| textTools.createSourceViewerConfiguraton(store, this)); |
| } |
| } |
| if (getSourceViewer() instanceof ScriptSourceViewer) { |
| ((ScriptSourceViewer) getSourceViewer()).setPreferenceStore(store); |
| } |
| if (occurrencesFinder != null) { |
| occurrencesFinder.setPreferenceStore(store); |
| } |
| } |
| |
| private ScriptOutlinePage createOutlinePage() { |
| final ScriptOutlinePage page = doCreateOutlinePage(); |
| fOutlineSelectionChangedListener.install(page); |
| setOutlinePageInput(page, getEditorInput()); |
| return page; |
| } |
| |
| /** |
| * Creates the outline page used with this editor. |
| * |
| * @return the created script outline page |
| */ |
| protected ScriptOutlinePage doCreateOutlinePage() { |
| return new ScriptOutlinePage(this, getPreferenceStore()); |
| } |
| |
| /** |
| * String identifiying concrete language editor. Used for ex. for fetching |
| * available filters |
| * |
| * @return |
| */ |
| public abstract String getEditorId(); |
| |
| /** |
| * Informs the editor that its outliner has been closed. |
| */ |
| @Override |
| public void outlinePageClosed() { |
| if (fOutlinePage != null) { |
| fOutlineSelectionChangedListener.uninstall(fOutlinePage); |
| fOutlinePage = null; |
| resetHighlightRange(); |
| } |
| } |
| |
| private void setOutlinePageInput(ScriptOutlinePage page, |
| IEditorInput input) { |
| if (page == null) { |
| return; |
| } |
| IModelElement me = getInputModelElement(); |
| if (me != null && me.exists()) { |
| page.setInput(me); |
| } else { |
| page.setInput(null); |
| } |
| } |
| |
| /** |
| * The templates page. |
| * |
| * @since 3.0 |
| */ |
| private ScriptTemplatesPage fTemplatesPage; |
| |
| /** |
| * Creates the templates page used with this editor. |
| * |
| * @return the created script templates page |
| * @since 3.0 |
| */ |
| protected ScriptTemplatesPage createTemplatesPage() { |
| final IDLTKUILanguageToolkit uiToolkit = getUILanguageToolkit(); |
| if (uiToolkit == null) { |
| return null; |
| } |
| final ITemplateAccess templateAccess = uiToolkit.getEditorTemplates(); |
| if (templateAccess == null) { |
| return null; |
| } |
| try { |
| return new ScriptTemplatesPage(this, templateAccess); |
| } catch (Throwable e) { |
| return null; |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Override |
| public <T> T getAdapter(Class<T> required) { |
| if (ITemplatesPage.class.equals(required)) { |
| if (fTemplatesPage == null) |
| fTemplatesPage = createTemplatesPage(); |
| return (T) fTemplatesPage; |
| } |
| if (IContentOutlinePage.class.equals(required)) { |
| if (fOutlinePage == null) |
| fOutlinePage = createOutlinePage(); |
| return (T) fOutlinePage; |
| } |
| if (required == IShowInTargetList.class) { |
| return (T) (IShowInTargetList) () -> new String[] { |
| DLTKUIPlugin.ID_SCRIPT_EXPLORER, IPageLayout.ID_OUTLINE }; |
| } |
| if (required == OccurrencesFinder.class) { |
| return (T) occurrencesFinder; |
| } |
| if (required == IFoldingStructureProvider.class) |
| return (T) fProjectionModelUpdater; |
| if (required == IFoldingStructureProviderExtension.class) |
| return (T) fProjectionModelUpdater; |
| |
| if (fProjectionSupport != null) { |
| Object adapter = fProjectionSupport.getAdapter(getSourceViewer(), |
| required); |
| if (adapter != null) |
| return (T) adapter; |
| } |
| |
| return super.getAdapter(required); |
| } |
| |
| /** |
| * Returns the mutex for the reconciler. See |
| * https://bugs.eclipse.org/bugs/show_bug.cgi?id=63898 for a description of |
| * the problem. |
| * <p> |
| * XXX remove once the underlying problem |
| * (https://bugs.eclipse.org/bugs/show_bug.cgi?id=66176) is solved. |
| * </p> |
| * |
| * @return the lock reconcilers may use to synchronize on |
| */ |
| public Object getReconcilerLock() { |
| return fReconcilerLock; |
| } |
| |
| protected void doSelectionChanged(SelectionChangedEvent event) { |
| ISourceReference reference = null; |
| ISelection selection = event.getSelection(); |
| Iterator<?> iter = ((IStructuredSelection) selection).iterator(); |
| while (iter.hasNext()) { |
| Object o = iter.next(); |
| if (o instanceof ISourceReference) { |
| reference = (ISourceReference) o; |
| break; |
| } |
| } |
| if (!isActivePart() && DLTKUIPlugin.getActivePage() != null) |
| DLTKUIPlugin.getActivePage().bringToTop(this); |
| setSelection(reference, !isActivePart()); |
| if (occurrencesFinder != null) { |
| occurrencesFinder.updateOccurrenceAnnotations(); |
| } |
| } |
| |
| protected boolean isActivePart() { |
| IWorkbenchPart part = getActivePart(); |
| return part != null && part.equals(this); |
| } |
| |
| private IWorkbenchPart getActivePart() { |
| IWorkbenchWindow window = getSite().getWorkbenchWindow(); |
| IPartService service = window.getPartService(); |
| IWorkbenchPart part = service.getActivePart(); |
| return part; |
| } |
| |
| protected void setSelection(ISourceReference reference, |
| boolean moveCursor) { |
| if (getSelectionProvider() == null) |
| return; |
| ISelection selection = getSelectionProvider().getSelection(); |
| if (selection instanceof TextSelection) { |
| TextSelection textSelection = (TextSelection) selection; |
| // PR 39995: [navigation] Forward history cleared after going back |
| // in navigation history: |
| // mark only in navigation history if the cursor is being moved |
| // (which it isn't if |
| // this is called from a PostSelectionEvent that should only update |
| // the magnet) |
| if (moveCursor && (textSelection.getOffset() != 0 |
| || textSelection.getLength() != 0)) |
| markInNavigationHistory(); |
| } |
| if (reference != null) { |
| StyledText textWidget = null; |
| ISourceViewer sourceViewer = getSourceViewer(); |
| if (sourceViewer != null) |
| textWidget = sourceViewer.getTextWidget(); |
| if (textWidget == null) |
| return; |
| try { |
| ISourceRange range = null; |
| range = reference.getSourceRange(); |
| if (range == null) |
| return; |
| int offset = range.getOffset(); |
| int length = range.getLength(); |
| if (offset < 0 || length < 0) |
| return; |
| setHighlightRange(offset, length, moveCursor); |
| if (!moveCursor) |
| return; |
| offset = -1; |
| length = -1; |
| if (reference instanceof IMember) { |
| range = ((IMember) reference).getNameRange(); |
| if (range != null) { |
| offset = range.getOffset(); |
| length = range.getLength(); |
| } |
| } else if (reference instanceof ILocalVariable) { |
| range = ((ILocalVariable) reference).getNameRange(); |
| if (range != null) { |
| offset = range.getOffset(); |
| length = range.getLength(); |
| } |
| } else if (reference instanceof IImportDeclaration |
| || reference instanceof IPackageDeclaration) { |
| // range is still getSourceRange() |
| offset = range.getOffset(); |
| length = range.getLength(); |
| } |
| if (offset > -1 && length > 0) { |
| try { |
| textWidget.setRedraw(false); |
| sourceViewer.revealRange(offset, length); |
| sourceViewer.setSelectedRange(offset, length); |
| } finally { |
| textWidget.setRedraw(true); |
| } |
| markInNavigationHistory(); |
| } |
| } catch (ModelException x) { |
| } catch (IllegalArgumentException x) { |
| } |
| } else if (moveCursor) { |
| resetHighlightRange(); |
| markInNavigationHistory(); |
| } |
| } |
| |
| @Override |
| protected void doSetSelection(ISelection selection) { |
| super.doSetSelection(selection); |
| synchronizeOutlinePageSelection(); |
| } |
| |
| @Override |
| public void setSelection(IModelElement element) { |
| |
| if (element == null || element instanceof ISourceModule) { |
| /* |
| * If the element is an ISourceModule this unit is either the input |
| * of this editor or not being displayed. In both cases, nothing |
| * should happened. |
| * (http://dev.eclipse.org/bugs/show_bug.cgi?id=5128) |
| */ |
| return; |
| } |
| |
| IModelElement corresponding = getCorrespondingElement(element); |
| if (corresponding instanceof ISourceReference) { |
| ISourceReference reference = (ISourceReference) corresponding; |
| // set highlight range |
| setSelection(reference, true); |
| // set outliner selection |
| if (fOutlinePage != null) { |
| fOutlineSelectionChangedListener.uninstall(fOutlinePage); |
| fOutlinePage.select(reference); |
| fOutlineSelectionChangedListener.install(fOutlinePage); |
| } |
| } |
| } |
| |
| /** |
| * Synchronizes the outliner selection with the given element position in |
| * the editor. |
| * |
| * @param element |
| * thescriptelement to select |
| */ |
| protected void synchronizeOutlinePage(ISourceReference element) { |
| synchronizeOutlinePage(element, true); |
| } |
| |
| /** |
| * Synchronizes the outliner selection with the given element position in |
| * the editor. |
| * |
| * @param element |
| * thescriptelement to select |
| * @param checkIfOutlinePageActive |
| * <code>true</code> if check for active outline page needs to be |
| * done |
| * @since 2.0 |
| */ |
| @Override |
| public void synchronizeOutlinePage(ISourceReference element, |
| boolean checkIfOutlinePageActive) { |
| if (fOutlinePage != null && element != null |
| && !(checkIfOutlinePageActive && isOutlinePageActive())) { |
| fOutlineSelectionChangedListener.uninstall(fOutlinePage); |
| fOutlinePage.select(element); |
| fOutlineSelectionChangedListener.install(fOutlinePage); |
| } |
| } |
| |
| /** |
| * Synchronizes the outliner selection with the actual cursor position in |
| * the editor. |
| */ |
| public void synchronizeOutlinePageSelection() { |
| synchronizeOutlinePage(computeHighlightRangeSourceReference()); |
| } |
| |
| private boolean isOutlinePageActive() { |
| IWorkbenchPart part = getActivePart(); |
| return part instanceof ContentOutline |
| && ((ContentOutline) part).getCurrentPage() == fOutlinePage; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * <p> |
| * Overrides the default implementation to handle {@link IJavaAnnotation}. |
| * </p> |
| * |
| * @param offset |
| * the region offset |
| * @param length |
| * the region length |
| * @param forward |
| * <code>true</code> for forwards, <code>false</code> for |
| * backward |
| * @param annotationPosition |
| * the position of the found annotation |
| * @return the found annotation |
| */ |
| @Override |
| protected Annotation findAnnotation(final int offset, final int length, |
| boolean forward, Position annotationPosition) { |
| |
| Annotation nextAnnotation = null; |
| Position nextAnnotationPosition = null; |
| Annotation containingAnnotation = null; |
| Position containingAnnotationPosition = null; |
| boolean currentAnnotation = false; |
| |
| IDocument document = getDocumentProvider() |
| .getDocument(getEditorInput()); |
| int endOfDocument = document.getLength(); |
| int distance = Integer.MAX_VALUE; |
| |
| IAnnotationModel model = getDocumentProvider() |
| .getAnnotationModel(getEditorInput()); |
| Iterator<Annotation> e = new ScriptAnnotationIterator(model, true); |
| while (e.hasNext()) { |
| Annotation a = e.next(); |
| if ((a instanceof IScriptAnnotation) |
| && ((IScriptAnnotation) a).hasOverlay() |
| || !isNavigationTarget(a)) |
| continue; |
| |
| Position p = model.getPosition(a); |
| if (p == null) |
| continue; |
| |
| if (forward && p.offset == offset || !forward |
| && p.offset + p.getLength() == offset + length) {// || |
| // p.includes(offset)) |
| // { |
| if (containingAnnotation == null || (forward |
| && p.length >= containingAnnotationPosition.length |
| || !forward |
| && p.length >= containingAnnotationPosition.length)) { |
| containingAnnotation = a; |
| containingAnnotationPosition = p; |
| currentAnnotation = p.length == length; |
| } |
| } else { |
| int currentDistance = 0; |
| |
| if (forward) { |
| currentDistance = p.getOffset() - offset; |
| if (currentDistance < 0) |
| currentDistance = endOfDocument + currentDistance; |
| |
| if (currentDistance < distance |
| || currentDistance == distance |
| && p.length < nextAnnotationPosition.length) { |
| distance = currentDistance; |
| nextAnnotation = a; |
| nextAnnotationPosition = p; |
| } |
| } else { |
| currentDistance = offset + length |
| - (p.getOffset() + p.length); |
| if (currentDistance < 0) |
| currentDistance = endOfDocument + currentDistance; |
| |
| if (currentDistance < distance |
| || currentDistance == distance |
| && p.length < nextAnnotationPosition.length) { |
| distance = currentDistance; |
| nextAnnotation = a; |
| nextAnnotationPosition = p; |
| } |
| } |
| } |
| } |
| if (containingAnnotationPosition != null |
| && (!currentAnnotation || nextAnnotation == null)) { |
| annotationPosition |
| .setOffset(containingAnnotationPosition.getOffset()); |
| annotationPosition |
| .setLength(containingAnnotationPosition.getLength()); |
| return containingAnnotation; |
| } |
| if (nextAnnotationPosition != null) { |
| annotationPosition.setOffset(nextAnnotationPosition.getOffset()); |
| annotationPosition.setLength(nextAnnotationPosition.getLength()); |
| } |
| |
| return nextAnnotation; |
| } |
| |
| /** |
| * Returns the annotation overlapping with the given range or |
| * <code>null</code>. |
| * |
| * @param offset |
| * the region offset |
| * @param length |
| * the region length |
| * @return the found annotation or <code>null</code> |
| * @since 3.0 |
| */ |
| private Annotation getAnnotation(int offset, int length) { |
| IAnnotationModel model = getDocumentProvider() |
| .getAnnotationModel(getEditorInput()); |
| Iterator<Annotation> e = new ScriptAnnotationIterator(model, false); |
| while (e.hasNext()) { |
| Annotation a = e.next(); |
| Position p = model.getPosition(a); |
| if (p != null && p.overlapsWith(offset, length)) |
| return a; |
| } |
| return null; |
| } |
| |
| /* |
| * @see |
| * org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#gotoAnnotation( |
| * boolean) |
| * |
| * @since 3.2 |
| */ |
| @Override |
| public Annotation gotoAnnotation(boolean forward) { |
| fSelectionChangedViaGotoAnnotation = true; |
| return super.gotoAnnotation(forward); |
| } |
| |
| /** |
| * 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 2.0 |
| */ |
| @Override |
| public ISourceReference computeHighlightRangeSourceReference() { |
| ISourceViewer sourceViewer = getSourceViewer(); |
| if (sourceViewer == null) |
| return null; |
| StyledText styledText = sourceViewer.getTextWidget(); |
| if (styledText == null) |
| return null; |
| int caret = 0; |
| if (sourceViewer instanceof ITextViewerExtension5) { |
| ITextViewerExtension5 extension = (ITextViewerExtension5) sourceViewer; |
| caret = extension |
| .widgetOffset2ModelOffset(styledText.getCaretOffset()); |
| } else { |
| int offset = sourceViewer.getVisibleRegion().getOffset(); |
| caret = offset + styledText.getCaretOffset(); |
| } |
| IModelElement element = getElementAt(caret, false); |
| if (!(element instanceof ISourceReference)) |
| return null; |
| // if (element.getElementType() == IModelElement.IMPORT_DECLARATION) { |
| // |
| // IImportDeclaration declaration= (IImportDeclaration) element; |
| // IImportContainer container= (IImportContainer) |
| // declaration.getParent(); |
| // ISourceRange srcRange= null; |
| // |
| // try { |
| // srcRange= container.getSourceRange(); |
| // } catch (ModelException e) { |
| // } |
| // |
| // if (srcRange != null && srcRange.getOffset() == caret) |
| // return container; |
| // } |
| return (ISourceReference) element; |
| } |
| |
| @Override |
| public void createPartControl(Composite parent) { |
| super.createPartControl(parent); |
| |
| IInformationControlCreator informationControlCreator = shell -> { |
| boolean cutDown = false; |
| // int style = cutDown ? SWT.NONE : (SWT.V_SCROLL | SWT.H_SCROLL); |
| // return new DefaultInformationControl(shell, SWT.RESIZE |
| // | SWT.TOOL, style, new HTMLTextPresenter(cutDown)); |
| if (BrowserInformationControl.isAvailable(shell)) |
| return new BrowserInformationControl(shell, |
| JFaceResources.DIALOG_FONT, true); |
| else |
| return new DefaultInformationControl(shell, |
| new HTMLTextPresenter(cutDown)); |
| }; |
| |
| fInformationPresenter = new InformationPresenter( |
| informationControlCreator); |
| fInformationPresenter.setSizeConstraints(60, 10, true, true); |
| fInformationPresenter.install(getSourceViewer()); |
| fInformationPresenter |
| .setDocumentPartitioning(IDocument.DEFAULT_CONTENT_TYPE); |
| |
| fEditorSelectionChangedListener = new EditorSelectionChangedListener(); |
| fEditorSelectionChangedListener.install(getSelectionProvider()); |
| if (true) |
| installSemanticHighlighting(); |
| if (!isEditable()) { |
| /* |
| * Manually call semantic highlighting for read only editor, since |
| * usually it's done from reconciler, but |
| * ScriptSourceViewerConfiguration.getReconciler(ISourceViewer) |
| * doesn't create reconciler for read only editor. |
| */ |
| updateSemanticHighlighting(); |
| } |
| if (occurrencesFinder != null) { |
| occurrencesFinder.install(); |
| } |
| } |
| |
| /** |
| * React to changed selection. |
| * |
| * |
| */ |
| protected void selectionChanged() { |
| if (getSelectionProvider() == null) |
| return; |
| ISourceReference element = computeHighlightRangeSourceReference(); |
| if (getPreferenceStore().getBoolean( |
| PreferenceConstants.EDITOR_SYNC_OUTLINE_ON_CURSOR_MOVE)) |
| synchronizeOutlinePage(element); |
| setSelection(element, false); |
| if (!fSelectionChangedViaGotoAnnotation) |
| updateStatusLine(); |
| fSelectionChangedViaGotoAnnotation = false; |
| } |
| |
| protected void updateStatusLine() { |
| ITextSelection selection = (ITextSelection) getSelectionProvider() |
| .getSelection(); |
| Annotation annotation = getAnnotation(selection.getOffset(), |
| selection.getLength()); |
| setStatusLineErrorMessage(null); |
| setStatusLineMessage(null); |
| if (annotation != null) { |
| updateMarkerViews(annotation); |
| if (annotation instanceof IScriptAnnotation |
| && ((IScriptAnnotation) annotation).isProblem()) |
| setStatusLineMessage(annotation.getText()); |
| } |
| } |
| |
| /** |
| * Returns the model element wrapped by this editors input. |
| * |
| * @return the model element wrapped by this editors input. |
| * |
| */ |
| public IModelElement getInputModelElement() { |
| return EditorUtility.getEditorInputModelElement(this, false); |
| } |
| |
| /** |
| * Returns thescriptelement of this editor's input corresponding to the |
| * given IModelElement. |
| * |
| * @param element |
| * thescriptelement |
| * @return the corresponding model element |
| */ |
| protected IModelElement getCorrespondingElement(IModelElement element) { |
| return element; |
| } |
| |
| /** |
| * Returns the most narrow model element including the given offset. |
| * |
| * @param offset |
| * the offset inside of the requested element |
| * @return the most narrow model element |
| */ |
| @Override |
| public IModelElement getElementAt(int offset) { |
| return getElementAt(offset, true); |
| } |
| |
| /** |
| * Returns the most narrow element including the given offset. If |
| * <code>reconcile</code> is <code>true</code> the editor's input element is |
| * reconciled in advance. If it is <code>false</code> this method only |
| * returns a result if the editor's input element does not need to be |
| * reconciled. |
| * |
| * @param offset |
| * the offset included by the retrieved element |
| * @param reconcile |
| * <code>true</code> if working copy should be reconciled |
| * @return the most narrow element which includes the given offset |
| */ |
| public IModelElement getElementAt(int offset, boolean reconcile) { |
| ISourceModule unit = (ISourceModule) getInputModelElement(); |
| if (unit != null) { |
| try { |
| if (reconcile) { |
| ScriptModelUtil.reconcile(unit); |
| return unit.getElementAt(offset); |
| } else if (unit.isConsistent()) |
| return unit.getElementAt(offset); |
| } catch (ModelException x) { |
| if (!x.isDoesNotExist()) |
| // DLTKUIPlugin.log(x.getStatus()); |
| System.err.println(x.getStatus()); |
| // nothing found, be tolerant and go on |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * The folding runner. |
| * |
| * |
| */ |
| private ToggleFoldingRunner fFoldingRunner; |
| |
| /** |
| * Tells whether the selection changed event is caused by a call to |
| * {@link #gotoAnnotation(boolean)}. |
| * |
| */ |
| private boolean fSelectionChangedViaGotoAnnotation; |
| |
| /** |
| * Runner that will toggle folding either instantly (if the editor is |
| * visible) or the next time it becomes visible. If a runner is started when |
| * there is already one registered, the registered one is canceled as |
| * toggling folding twice is a no-op. |
| * <p> |
| * The access to the fFoldingRunner field is not thread-safe, it is assumed |
| * that <code>runWhenNextVisible</code> is only called from the UI thread. |
| * </p> |
| * |
| * |
| */ |
| protected final class ToggleFoldingRunner implements IPartListener2 { |
| public ToggleFoldingRunner() { |
| } |
| |
| /** |
| * The workbench page we registered the part listener with, or |
| * <code>null</code>. |
| */ |
| private IWorkbenchPage fPage; |
| |
| /** |
| * Does the actual toggling of projection. |
| */ |
| private void toggleFolding() { |
| ISourceViewer sourceViewer = getSourceViewer(); |
| if (sourceViewer instanceof ProjectionViewer) { |
| ProjectionViewer pv = (ProjectionViewer) sourceViewer; |
| if (pv.isProjectionMode() != isFoldingEnabled()) { |
| if (pv.canDoOperation(ProjectionViewer.TOGGLE)) |
| pv.doOperation(ProjectionViewer.TOGGLE); |
| } |
| } |
| } |
| |
| /** |
| * Makes sure that the editor's folding state is correct the next time |
| * it becomes visible. If it already is visible, it toggles the folding |
| * state. If not, it either registers a part listener to toggle folding |
| * when the editor becomes visible, or cancels an already registered |
| * runner. |
| */ |
| public void runWhenNextVisible() { |
| // if there is one already: toggling twice is the identity |
| if (fFoldingRunner != null) { |
| fFoldingRunner.cancel(); |
| return; |
| } |
| IWorkbenchPartSite site = getSite(); |
| if (site != null) { |
| IWorkbenchPage page = site.getPage(); |
| if (!page.isPartVisible(ScriptEditor.this)) { |
| // if we're not visible - defer until visible |
| fPage = page; |
| fFoldingRunner = this; |
| page.addPartListener(this); |
| return; |
| } |
| } |
| // we're visible - run now |
| toggleFolding(); |
| } |
| |
| /** |
| * Remove the listener and clear the field. |
| */ |
| private void cancel() { |
| if (fPage != null) { |
| fPage.removePartListener(this); |
| fPage = null; |
| } |
| if (fFoldingRunner == this) |
| fFoldingRunner = null; |
| } |
| |
| /* |
| * @seeorg.eclipse.ui.IPartListener2#partVisible(org.eclipse.ui. |
| * IWorkbenchPartReference) |
| */ |
| @Override |
| public void partVisible(IWorkbenchPartReference partRef) { |
| if (ScriptEditor.this.equals(partRef.getPart(false))) { |
| cancel(); |
| toggleFolding(); |
| } |
| } |
| |
| /* |
| * @seeorg.eclipse.ui.IPartListener2#partClosed(org.eclipse.ui. |
| * IWorkbenchPartReference) |
| */ |
| @Override |
| public void partClosed(IWorkbenchPartReference partRef) { |
| if (ScriptEditor.this.equals(partRef.getPart(false))) { |
| cancel(); |
| } |
| } |
| |
| @Override |
| public void partActivated(IWorkbenchPartReference partRef) { |
| } |
| |
| @Override |
| public void partBroughtToTop(IWorkbenchPartReference partRef) { |
| } |
| |
| @Override |
| public void partDeactivated(IWorkbenchPartReference partRef) { |
| } |
| |
| @Override |
| public void partOpened(IWorkbenchPartReference partRef) { |
| } |
| |
| @Override |
| public void partHidden(IWorkbenchPartReference partRef) { |
| } |
| |
| @Override |
| public void partInputChanged(IWorkbenchPartReference partRef) { |
| } |
| } |
| |
| /** |
| * Creates folding structure provider to use in this editor. Default |
| * implementation queries the |
| * <code>org.eclipse.dltk.ui.folding/structureProvider</code> extension |
| * point. |
| * |
| * @return folding structure provider or <code>null</code>. |
| */ |
| protected IFoldingStructureProvider createFoldingStructureProvider() { |
| return getFoldingStructureProvider(); |
| } |
| |
| /** |
| * Returns folding structure provider. |
| * |
| * @return |
| */ |
| @Deprecated |
| protected IFoldingStructureProvider getFoldingStructureProvider() { |
| return FoldingProviderManager.getStructureProvider(getNatureId()); |
| } |
| |
| private boolean isEditorHoverProperty(String property) { |
| return PreferenceConstants.EDITOR_TEXT_HOVER_MODIFIERS.equals(property); |
| } |
| |
| /* |
| * Update the hovering behavior depending on the preferences. |
| */ |
| private void updateHoverBehavior() { |
| SourceViewerConfiguration configuration = getSourceViewerConfiguration(); |
| String[] types = configuration |
| .getConfiguredContentTypes(getSourceViewer()); |
| |
| for (int i = 0; i < types.length; i++) { |
| |
| String t = types[i]; |
| |
| ISourceViewer sourceViewer = getSourceViewer(); |
| if (sourceViewer instanceof ITextViewerExtension2) { |
| // Remove existing hovers |
| ((ITextViewerExtension2) sourceViewer).removeTextHovers(t); |
| |
| int[] stateMasks = configuration |
| .getConfiguredTextHoverStateMasks(getSourceViewer(), t); |
| |
| if (stateMasks != null) { |
| for (int j = 0; j < stateMasks.length; j++) { |
| int stateMask = stateMasks[j]; |
| ITextHover textHover = configuration |
| .getTextHover(sourceViewer, t, stateMask); |
| ((ITextViewerExtension2) sourceViewer) |
| .setTextHover(textHover, t, stateMask); |
| } |
| } else { |
| ITextHover textHover = configuration |
| .getTextHover(sourceViewer, t); |
| ((ITextViewerExtension2) sourceViewer).setTextHover( |
| textHover, t, |
| ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK); |
| } |
| } else |
| sourceViewer.setTextHover( |
| configuration.getTextHover(sourceViewer, t), t); |
| } |
| } |
| |
| @Override |
| protected void handlePreferenceStoreChanged(PropertyChangeEvent event) { |
| String property = event.getProperty(); |
| try { |
| |
| ISourceViewer sourceViewer = getSourceViewer(); |
| if (sourceViewer == null) { |
| return; |
| } |
| boolean newBooleanValue = false; |
| Object newValue = event.getNewValue(); |
| |
| if (isEditorHoverProperty(property)) |
| updateHoverBehavior(); |
| |
| if (newValue != null) |
| newBooleanValue = Boolean.valueOf(newValue.toString()) |
| .booleanValue(); |
| if (PreferenceConstants.EDITOR_SYNC_OUTLINE_ON_CURSOR_MOVE |
| .equals(property)) { |
| if (newBooleanValue) |
| selectionChanged(); |
| return; |
| } |
| |
| if (occurrencesFinder != null && occurrencesFinder |
| .handlePreferenceStoreChanged(property, newBooleanValue)) { |
| return; |
| } |
| |
| if (CodeFormatterConstants.FORMATTER_TAB_SIZE.equals(property) |
| || CodeFormatterConstants.FORMATTER_INDENTATION_SIZE |
| .equals(property) |
| || CodeFormatterConstants.FORMATTER_TAB_CHAR |
| .equals(property)) { |
| if (CodeFormatterConstants.FORMATTER_TAB_CHAR |
| .equals(property)) { |
| if (isTabsToSpacesConversionEnabled()) |
| installTabsToSpacesConverter(); |
| else |
| uninstallTabsToSpacesConverter(); |
| } |
| updateIndentPrefixes(); |
| StyledText textWidget = sourceViewer.getTextWidget(); |
| int tabWidth = getSourceViewerConfiguration() |
| .getTabWidth(sourceViewer); |
| if (textWidget.getTabs() != tabWidth) |
| textWidget.setTabs(tabWidth); |
| return; |
| } |
| if (PreferenceConstants.EDITOR_SMART_TAB.equals(property)) { |
| if (getPreferenceStore() |
| .getBoolean(PreferenceConstants.EDITOR_SMART_TAB)) { |
| setActionActivationCode(DLTKActionConstants.INDENT_ON_TAB, |
| SWT.TAB, -1, SWT.NONE); |
| } else { |
| removeActionActivationCode( |
| DLTKActionConstants.INDENT_ON_TAB); |
| } |
| } |
| |
| if (isFoldingPropertyEvent(property) |
| && sourceViewer instanceof ProjectionViewer) { |
| handleFoldingPropertyEvent(property); |
| } |
| |
| final ScriptSourceViewerConfiguration ssvc = (ScriptSourceViewerConfiguration) getSourceViewerConfiguration(); |
| final IContentAssistant c = ((AdaptedSourceViewer) sourceViewer) |
| .getContentAssistant(); |
| if (c instanceof ContentAssistant) { |
| ssvc.changeContentAssistantConfiguration((ContentAssistant) c, |
| event); |
| } |
| ssvc.handlePropertyChangeEvent(event); |
| } finally { |
| super.handlePreferenceStoreChanged(event); |
| } |
| if (AbstractDecoratedTextEditorPreferenceConstants.SHOW_RANGE_INDICATOR |
| .equals(property)) { |
| // superclass already installed the range indicator |
| Object newValue = event.getNewValue(); |
| ISourceViewer viewer = getSourceViewer(); |
| if (newValue != null && viewer != null) { |
| if (Boolean.valueOf(newValue.toString()).booleanValue()) { |
| // adjust the highlightrange in order to get the magnet |
| // right after changing the selection |
| Point selection = viewer.getSelectedRange(); |
| adjustHighlightRange(selection.x, selection.y); |
| } |
| } |
| } |
| } |
| |
| @Override |
| protected boolean affectsTextPresentation(PropertyChangeEvent event) { |
| return ((ScriptSourceViewerConfiguration) getSourceViewerConfiguration()) |
| .affectsTextPresentation(event) |
| || super.affectsTextPresentation(event); |
| } |
| |
| protected void handleFoldingPropertyEvent(String property) { |
| // NOTE: 'initially fold' preferences do not require handling |
| if (PreferenceConstants.EDITOR_FOLDING_ENABLED.equals(property)) { |
| ToggleFoldingRunner runner = new ToggleFoldingRunner(); |
| runner.runWhenNextVisible(); |
| } else { |
| fProjectionModelUpdater.initialize(false); |
| } |
| } |
| |
| protected final boolean isFoldingPropertyEvent(String property) { |
| if (isHandledPropertyEvent(property, GLOBAL_FOLDING_PROPERTIES)) { |
| return true; |
| } |
| |
| if (isHandledPropertyEvent(property, getFoldingEventPreferenceKeys())) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Returns a string array containing the language specific folding |
| * preference keys that should be handed when a property change event is |
| * fired. |
| * |
| * <p> |
| * Default implementation returns an empty array. Subclasses should override |
| * this method to return folding keys that are language specific. |
| * </p> |
| */ |
| protected String[] getFoldingEventPreferenceKeys() { |
| return CharOperation.NO_STRINGS; |
| } |
| |
| /** |
| * Text navigation action to navigate to the next sub-word. |
| * |
| * |
| */ |
| protected abstract class NextSubWordAction extends TextNavigationAction { |
| protected DLTKWordIterator fIterator = new DLTKWordIterator(); |
| |
| /** |
| * 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); |
| } |
| |
| /* |
| * @see org.eclipse.jface.action.IAction#run() |
| */ |
| @Override |
| public void run() { |
| // Check whether we are in ascriptcode partition and the preference |
| // is enabled |
| final IPreferenceStore store = getPreferenceStore(); |
| if (!store.getBoolean( |
| PreferenceConstants.EDITOR_SUB_WORD_NAVIGATION)) { |
| super.run(); |
| return; |
| } |
| final ISourceViewer viewer = getSourceViewer(); |
| final IDocument document = viewer.getDocument(); |
| fIterator.setText((CharacterIterator) new DocumentCharacterIterator( |
| document)); |
| int position = widgetOffset2ModelOffset(viewer, |
| viewer.getTextWidget().getCaretOffset()); |
| if (position == -1) |
| return; |
| int next = findNextPosition(position); |
| if (next != BreakIterator.DONE) { |
| setCaretPosition(next); |
| getTextWidget().showSelection(); |
| fireSelectionChanged(); |
| } |
| } |
| |
| /** |
| * 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</code>. |
| * |
| * @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. |
| */ |
| 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. |
| */ |
| 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(); |
| final int caret, length; |
| Point selection = viewer.getSelectedRange(); |
| if (selection.y != 0) { |
| caret = selection.x; |
| length = selection.y; |
| } else { |
| caret = widgetOffset2ModelOffset(viewer, |
| viewer.getTextWidget().getCaretOffset()); |
| length = position - caret; |
| } |
| try { |
| viewer.getDocument().replace(caret, length, ""); //$NON-NLS-1$ |
| } catch (BadLocationException exception) { |
| // Should not happen |
| } |
| } |
| |
| @Override |
| protected int findNextPosition(int position) { |
| return fIterator.following(position); |
| } |
| |
| /* |
| * @see org.eclipse.ui.texteditor.IUpdate#update() |
| */ |
| @Override |
| public void update() { |
| setEnabled(isEditorInputModifiable()); |
| } |
| } |
| |
| /** |
| * Text operation action to select the next sub-word. |
| * |
| * |
| */ |
| protected class SelectNextSubWordAction extends NextSubWordAction { |
| /** |
| * Creates a new select next sub-word action. |
| */ |
| public SelectNextSubWordAction() { |
| super(ST.SELECT_WORD_NEXT); |
| } |
| |
| @Override |
| protected void setCaretPosition(final int position) { |
| final ISourceViewer viewer = getSourceViewer(); |
| final StyledText text = viewer.getTextWidget(); |
| if (text != null && !text.isDisposed()) { |
| final Point selection = text.getSelection(); |
| final int caret = text.getCaretOffset(); |
| final int offset = modelOffset2WidgetOffset(viewer, position); |
| if (caret == selection.x) |
| text.setSelectionRange(selection.y, offset - selection.y); |
| else |
| text.setSelectionRange(selection.x, offset - selection.x); |
| } |
| } |
| } |
| |
| /** |
| * Text navigation action to navigate to the previous sub-word. |
| * |
| * |
| */ |
| protected abstract class PreviousSubWordAction |
| extends TextNavigationAction { |
| protected DLTKWordIterator fIterator = new DLTKWordIterator(); |
| |
| /** |
| * 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); |
| } |
| |
| /* |
| * @see org.eclipse.jface.action.IAction#run() |
| */ |
| @Override |
| public void run() { |
| // Check whether we are in ascriptcode partition and the preference |
| // is enabled |
| final IPreferenceStore store = getPreferenceStore(); |
| if (!store.getBoolean( |
| PreferenceConstants.EDITOR_SUB_WORD_NAVIGATION)) { |
| super.run(); |
| return; |
| } |
| final ISourceViewer viewer = getSourceViewer(); |
| final IDocument document = viewer.getDocument(); |
| fIterator.setText((CharacterIterator) new DocumentCharacterIterator( |
| document)); |
| int position = widgetOffset2ModelOffset(viewer, |
| viewer.getTextWidget().getCaretOffset()); |
| if (position == -1) |
| return; |
| int previous = findPreviousPosition(position); |
| if (previous != BreakIterator.DONE) { |
| setCaretPosition(previous); |
| getTextWidget().showSelection(); |
| fireSelectionChanged(); |
| } |
| } |
| |
| /** |
| * 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</code>. |
| * |
| * @param position |
| * Position where the action should move the caret |
| */ |
| protected abstract void setCaretPosition(int position); |
| } |
| |
| /** |
| * Text navigation action to navigate to the previous sub-word. |
| */ |
| protected 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. |
| */ |
| 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(); |
| Point selection = viewer.getSelectedRange(); |
| if (selection.y != 0) { |
| position = selection.x; |
| length = selection.y; |
| } else { |
| length = widgetOffset2ModelOffset(viewer, |
| viewer.getTextWidget().getCaretOffset()) - position; |
| } |
| try { |
| viewer.getDocument().replace(position, length, ""); //$NON-NLS-1$ |
| } catch (BadLocationException exception) { |
| // 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. |
| */ |
| 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); |
| } |
| } |
| } |
| |
| /* |
| * @see AbstractTextEditor#createNavigationActions() |
| */ |
| @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); |
| } |
| |
| @Override |
| protected final ISourceViewer createSourceViewer(Composite parent, |
| IVerticalRuler verticalRuler, int styles) { |
| |
| IPreferenceStore store = getPreferenceStore(); |
| ISourceViewer viewer = createScriptSourceViewer(parent, verticalRuler, |
| getOverviewRuler(), isOverviewRulerVisible(), styles, store); |
| |
| if (DLTKCore.DEBUG) { |
| System.err.println("Create help contexts"); //$NON-NLS-1$ |
| } |
| // ScriptUIHelp.setHelp(this, viewer.getTextWidget(), |
| // IScriptHelpContextIds.JAVA_EDITOR); |
| |
| ScriptSourceViewer scriptSourceViewer = null; |
| if (viewer instanceof ScriptSourceViewer) |
| scriptSourceViewer = (ScriptSourceViewer) viewer; |
| |
| /* |
| * This is a performance optimization to reduce the computation of the |
| * text presentation triggered by {@link #setVisibleDocument(IDocument)} |
| */ |
| if (scriptSourceViewer != null && isFoldingEnabled() && (store == null |
| || !store.getBoolean(PreferenceConstants.EDITOR_SHOW_SEGMENTS))) |
| scriptSourceViewer.prepareDelayedProjection(); |
| |
| ProjectionViewer projectionViewer = (ProjectionViewer) viewer; |
| fProjectionSupport = new ProjectionSupport(projectionViewer, |
| getAnnotationAccess(), getSharedColors()); |
| fProjectionSupport.addSummarizableAnnotationType( |
| "org.eclipse.ui.workbench.texteditor.error"); //$NON-NLS-1$ |
| fProjectionSupport.addSummarizableAnnotationType( |
| "org.eclipse.ui.workbench.texteditor.warning"); //$NON-NLS-1$ |
| final IDLTKLanguageToolkit toolkit = this.getLanguageToolkit(); |
| fProjectionSupport.setHoverControlCreator(shell -> { |
| int shellStyle = SWT.TOOL | SWT.NO_TRIM | getOrientation(); |
| String statusFieldText = EditorsUI.getTooltipAffordanceString(); |
| return new SourceViewerInformationControl(shell, shellStyle, |
| SWT.NONE, statusFieldText, toolkit); |
| }); |
| fProjectionSupport.setInformationPresenterControlCreator(shell -> { |
| int shellStyle = SWT.RESIZE | SWT.TOOL | getOrientation(); |
| int style = SWT.V_SCROLL | SWT.H_SCROLL; |
| return new SourceViewerInformationControl(shell, shellStyle, style, |
| toolkit); |
| }); |
| |
| fProjectionSupport.install(); |
| |
| fProjectionModelUpdater = createFoldingStructureProvider(); |
| if (fProjectionModelUpdater != null) |
| fProjectionModelUpdater.install(this, projectionViewer, |
| getPreferenceStore()); |
| |
| // ensure source viewer decoration support has been created and |
| // configured |
| getSourceViewerDecorationSupport(viewer); |
| |
| return viewer; |
| } |
| |
| protected ISourceViewer createScriptSourceViewer(Composite parent, |
| IVerticalRuler verticalRuler, IOverviewRuler overviewRuler, |
| boolean isOverviewRulerVisible, int styles, |
| IPreferenceStore store) { |
| return new AdaptedSourceViewer(parent, verticalRuler, |
| getOverviewRuler(), isOverviewRulerVisible(), styles, store); |
| } |
| |
| /** |
| * Resets the foldings structure according to the folding preferences. |
| */ |
| public void resetProjection() { |
| if (fProjectionModelUpdater != null) { |
| fProjectionModelUpdater.initialize(); |
| } |
| } |
| |
| /** |
| * Collapses all foldable members if supported by the folding structure |
| * provider. |
| * |
| * |
| */ |
| public void collapseMembers() { |
| if (fProjectionModelUpdater instanceof IFoldingStructureProviderExtension) { |
| IFoldingStructureProviderExtension extension = (IFoldingStructureProviderExtension) fProjectionModelUpdater; |
| extension.collapseMembers(); |
| } |
| } |
| |
| /** |
| * Collapses all foldable comments if supported by the folding structure |
| * provider. |
| * |
| * |
| */ |
| public void collapseComments() { |
| if (fProjectionModelUpdater instanceof IFoldingStructureProviderExtension) { |
| IFoldingStructureProviderExtension extension = (IFoldingStructureProviderExtension) fProjectionModelUpdater; |
| extension.collapseComments(); |
| } |
| } |
| |
| /* |
| * @see AbstractTextEditor#rulerContextMenuAboutToShow(IMenuManager) |
| */ |
| @Override |
| protected void rulerContextMenuAboutToShow(IMenuManager menu) { |
| super.rulerContextMenuAboutToShow(menu); |
| IMenuManager foldingMenu = new MenuManager( |
| DLTKEditorMessages.Editor_FoldingMenu_name, "projection"); //$NON-NLS-1$ |
| menu.appendToGroup(ITextEditorActionConstants.GROUP_RULERS, |
| foldingMenu); |
| |
| IAction action = getAction("FoldingToggle"); //$NON-NLS-1$ |
| if (action != null) { |
| foldingMenu.add(action); |
| } |
| action = getAction("FoldingExpandAll"); //$NON-NLS-1$ |
| if (action != null) { |
| foldingMenu.add(action); |
| } |
| action = getAction("FoldingCollapseAll"); //$NON-NLS-1$ |
| if (action != null) { |
| foldingMenu.add(action); |
| } |
| action = getAction("FoldingRestore"); //$NON-NLS-1$ |
| if (action != null) { |
| foldingMenu.add(action); |
| } |
| action = getAction("FoldingCollapseMembers"); //$NON-NLS-1$ |
| if (action != null) { |
| foldingMenu.add(action); |
| } |
| action = getAction("FoldingCollapseComments"); //$NON-NLS-1$ |
| if (action != null) { |
| foldingMenu.add(action); |
| } |
| } |
| |
| /* |
| * @see org.eclipse.ui.texteditor.AbstractTextEditor#performRevert() |
| */ |
| @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, |
| getPreferenceStore()); |
| projectionViewer.enableProjection(); |
| } |
| |
| } finally { |
| projectionViewer.setRedraw(true); |
| } |
| } |
| |
| protected String getNatureId() { |
| return getLanguageToolkit().getNatureId(); |
| } |
| |
| @Override |
| public abstract IDLTKLanguageToolkit getLanguageToolkit(); |
| |
| protected IDLTKUILanguageToolkit getUILanguageToolkit() { |
| return DLTKUILanguageManager.getLanguageToolkit(getNatureId()); |
| } |
| |
| /** |
| * Return identifier of call hierarchy. Used by call hierarchy actions. |
| * |
| * @return |
| */ |
| public String getCallHierarchyID() { |
| return null; |
| } |
| |
| /* |
| * @see AbstractTextEditor#performSave(boolean, IProgressMonitor) |
| */ |
| @Override |
| protected void performSave(boolean overwrite, |
| IProgressMonitor progressMonitor) { |
| IDocumentProvider p = getDocumentProvider(); |
| if (p instanceof ISourceModuleDocumentProvider) { |
| ISourceModuleDocumentProvider cp = (ISourceModuleDocumentProvider) p; |
| cp.setSavePolicy(fSavePolicy); |
| } |
| try { |
| super.performSave(overwrite, progressMonitor); |
| } finally { |
| if (p instanceof ISourceModuleDocumentProvider) { |
| ISourceModuleDocumentProvider cp = (ISourceModuleDocumentProvider) p; |
| cp.setSavePolicy(null); |
| } |
| } |
| } |
| |
| /* |
| * @see AbstractTextEditor#doSave(IProgressMonitor) |
| */ |
| @Override |
| public void doSave(IProgressMonitor progressMonitor) { |
| |
| IDocumentProvider p = getDocumentProvider(); |
| if (p == null) { |
| // editor has been closed |
| return; |
| } |
| |
| if (p.isDeleted(getEditorInput())) { |
| |
| if (isSaveAsAllowed()) { |
| |
| /* |
| * 1GEUSSR: ITPUI:ALL - User should never loose changes made in |
| * the editors. Changed Behavior to make sure that if called |
| * inside a regular save (because of deletion of input element) |
| * there is a way to report back to the caller. |
| */ |
| performSaveAs(progressMonitor); |
| |
| } else { |
| |
| /* |
| * 1GF5YOX: ITPJUI:ALL - Save of delete file claims it's still |
| * there Missing resources. |
| */ |
| Shell shell = getSite().getShell(); |
| MessageDialog.openError(shell, |
| DLTKEditorMessages.SourceModuleEditor_error_saving_title1, |
| DLTKEditorMessages.SourceModuleEditor_error_saving_message1); |
| } |
| |
| } else { |
| |
| setStatusLineErrorMessage(null); |
| |
| updateState(getEditorInput()); |
| validateState(getEditorInput()); |
| |
| IWorkingCopyManager manager = DLTKUIPlugin.getDefault() |
| .getWorkingCopyManager(); |
| ISourceModule unit = manager.getWorkingCopy(getEditorInput()); |
| |
| if (unit != null) { |
| // synchronized (unit) { |
| performSave(false, progressMonitor); |
| // } |
| } else |
| performSave(false, progressMonitor); |
| } |
| } |
| |
| /** |
| * 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); |
| } |
| |
| protected final static char[] BRACKETS = { '{', '}', '(', ')', '[', ']' }; |
| |
| protected static boolean isBracket(char character) { |
| for (int i = 0; i != BRACKETS.length; ++i) |
| if (character == BRACKETS[i]) |
| return true; |
| return false; |
| } |
| |
| protected 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 ICharacterPairMatcher fBracketMatcher; |
| |
| /** |
| * Returns the bracket matcher for this editor, delegates to |
| * {@link #createBracketMatcher()} to actually create it. |
| * |
| * @return the bracket matcher or <code>null</code> |
| */ |
| protected final ICharacterPairMatcher getBracketMatcher() { |
| if (fBracketMatcher == null) { |
| fBracketMatcher = createBracketMatcher(); |
| } |
| return fBracketMatcher; |
| } |
| |
| /** |
| * Override in your editor class to create bracket matcher for your |
| * language. |
| * |
| * @return |
| */ |
| protected ICharacterPairMatcher createBracketMatcher() { |
| return null; |
| } |
| |
| @Override |
| protected void configureSourceViewerDecorationSupport( |
| SourceViewerDecorationSupport support) { |
| configureBracketMatcher(support); |
| super.configureSourceViewerDecorationSupport(support); |
| } |
| |
| protected void configureBracketMatcher( |
| SourceViewerDecorationSupport support) { |
| final ICharacterPairMatcher bracketMatcher = getBracketMatcher(); |
| if (bracketMatcher != null) { |
| support.setCharacterPairMatcher(bracketMatcher); |
| support.setMatchingCharacterPainterPreferenceKeys(MATCHING_BRACKETS, |
| MATCHING_BRACKETS_COLOR); |
| } |
| } |
| |
| /** |
| * Jumps to the matching bracket. |
| */ |
| public void gotoMatchingBracket() { |
| final ICharacterPairMatcher bracketMatcher = getBracketMatcher(); |
| if (bracketMatcher == null) { |
| return; |
| } |
| 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( |
| DLTKEditorMessages.ScriptEditor_nobracketSelected); |
| sourceViewer.getTextWidget().getDisplay().beep(); |
| return; |
| } |
| |
| // #26314 |
| int sourceCaretOffset = selection.getOffset() + selection.getLength(); |
| if (isSurroundedByBrackets(document, sourceCaretOffset)) |
| sourceCaretOffset -= selection.getLength(); |
| |
| IRegion region = bracketMatcher.match(document, sourceCaretOffset); |
| if (region == null) { |
| setStatusLineErrorMessage( |
| DLTKEditorMessages.ScriptEditor_noMatchingBracketFound); |
| sourceViewer.getTextWidget().getDisplay().beep(); |
| return; |
| } |
| |
| int offset = region.getOffset(); |
| int length = region.getLength(); |
| |
| if (length < 1) |
| return; |
| |
| int anchor = bracketMatcher.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( |
| DLTKEditorMessages.ScriptEditor_matchingBracketIsOutsideSelectedElement); |
| sourceViewer.getTextWidget().getDisplay().beep(); |
| return; |
| } |
| |
| if (selection.getLength() < 0) |
| targetOffset -= selection.getLength(); |
| |
| sourceViewer.setSelectedRange(targetOffset, selection.getLength()); |
| sourceViewer.revealRange(targetOffset, selection.getLength()); |
| } |
| |
| public void updatedTitleImage(Image image) { |
| setTitleImage(image); |
| } |
| |
| private ListenerList<IScriptReconcilingListener> fReconcilingListeners = new ListenerList<>( |
| ListenerList.IDENTITY); |
| |
| /** |
| * Mutex for the reconciler. See |
| * https://bugs.eclipse.org/bugs/show_bug.cgi?id=63898 for a description of |
| * the problem. |
| * <p> |
| * XXX remove once the underlying problem |
| * (https://bugs.eclipse.org/bugs/show_bug.cgi?id=66176) is solved. |
| * </p> |
| */ |
| private final Object fReconcilerLock = new Object(); |
| |
| @Override |
| public void aboutToBeReconciled() { |
| |
| // Notify AST provider |
| // JavaPlugin.getDefault().getASTProvider().aboutToBeReconciled( |
| // getInputJavaElement()); |
| |
| // Notify listeners |
| for (IScriptReconcilingListener listener : fReconcilingListeners) |
| listener.aboutToBeReconciled(); |
| } |
| |
| /* |
| * @see |
| * org.eclipse.jdt.internal.ui.text.java.IJavaReconcilingListener#reconciled |
| * (CompilationUnit, boolean, IProgressMonitor) |
| * |
| * @since 3.0 |
| */ |
| @Override |
| public void reconciled(ISourceModule ast, boolean forced, |
| IProgressMonitor progressMonitor) { |
| |
| // see: https://bugs.eclipse.org/bugs/show_bug.cgi?id=58245 |
| // JavaPlugin javaPlugin= JavaPlugin.getDefault(); |
| // if (javaPlugin == null) |
| // return; |
| // |
| // // Always notify AST provider |
| // javaPlugin.getASTProvider().reconciled(ast, getInputJavaElement(), |
| // progressMonitor); |
| |
| // Notify listeners |
| for (IScriptReconcilingListener listener : fReconcilingListeners) |
| listener.reconciled(ast, forced, progressMonitor); |
| |
| // Update Outline page selection |
| if (!forced && !progressMonitor.isCanceled()) { |
| Shell shell = getSite().getShell(); |
| if (shell != null && !shell.isDisposed()) { |
| shell.getDisplay().asyncExec(() -> selectionChanged()); |
| } |
| } |
| } |
| |
| public void addReconcileListener( |
| IScriptReconcilingListener semanticHighlightingReconciler) { |
| fReconcilingListeners.add(semanticHighlightingReconciler); |
| } |
| |
| public void removeReconcileListener( |
| IScriptReconcilingListener semanticHighlightingReconciler) { |
| fReconcilingListeners.remove(semanticHighlightingReconciler); |
| } |
| |
| private SemanticHighlightingManager fSemanticManager; |
| |
| private void installSemanticHighlighting() { |
| ScriptTextTools textTools = getTextTools(); |
| if (fSemanticManager == null && textTools != null) { |
| final ISemanticHighlightingUpdater updater = textTools |
| .getSemanticPositionUpdater(getNatureId()); |
| if (updater != null) { |
| fSemanticManager = new SemanticHighlightingManager(updater); |
| fSemanticManager.install(this, |
| (ScriptSourceViewer) getSourceViewer(), |
| textTools.getColorManager(), getPreferenceStore()); |
| } |
| } |
| } |
| |
| private void updateSemanticHighlighting() { |
| final IModelElement element = getInputModelElement(); |
| if (!(element instanceof ISourceModule)) { |
| return; |
| } |
| Job job = new Job( |
| DLTKEditorMessages.ScriptEditor_InitializeSemanticHighlighting) { |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| if (fSemanticManager != null) { |
| SemanticHighlightingReconciler reconciler = fSemanticManager |
| .getReconciler(); |
| if (reconciler != null) |
| reconciler.reconciled((ISourceModule) element, false, |
| monitor); |
| } |
| return Status.OK_STATUS; |
| } |
| }; |
| job.setPriority(Job.DECORATE); |
| job.setSystem(true); |
| job.schedule(); |
| } |
| |
| /** |
| * Uninstall Semantic Highlighting. |
| * |
| * @since 3.0 |
| */ |
| private void uninstallSemanticHighlighting() { |
| if (fSemanticManager != null) { |
| fSemanticManager.uninstall(); |
| fSemanticManager = null; |
| } |
| } |
| |
| @Override |
| public int getOrientation() { |
| return SWT.LEFT_TO_RIGHT; |
| } |
| |
| @Override |
| protected String[] collectContextMenuPreferencePages() { |
| final List<String> result = new ArrayList<String>(); |
| final IDLTKUILanguageToolkit uiToolkit = getUILanguageToolkit(); |
| addPages(result, uiToolkit.getEditorPreferencePages()); |
| addPages(result, super.collectContextMenuPreferencePages()); |
| return result.toArray(new String[result.size()]); |
| } |
| |
| private void addPages(final List<String> result, final String[] pages) { |
| if (pages != null) { |
| for (int i = 0; i < pages.length; ++i) { |
| if (!result.contains(pages[i])) { |
| result.add(pages[i]); |
| } |
| } |
| } |
| } |
| |
| /* |
| * @see AbstractDecoratedTextEditor#isTabsToSpacesConversionEnabled() |
| */ |
| @Override |
| protected boolean isTabsToSpacesConversionEnabled() { |
| return getPreferenceStore() != null |
| && CodeFormatterConstants.SPACE.equals(getPreferenceStore() |
| .getString(CodeFormatterConstants.FORMATTER_TAB_CHAR)); |
| } |
| |
| private boolean isHandledPropertyEvent(String property, String[] handled) { |
| for (int i = 0; i < handled.length; i++) { |
| if (handled[i].equals(property)) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| protected String getSymbolicFontName() { |
| return getFontPropertyPreferenceKey(); |
| } |
| |
| /* |
| * Increase visibility for this package - called from {@link |
| * OccurrencesFinder} |
| */ |
| @Override |
| protected IProgressMonitor getProgressMonitor() { |
| return super.getProgressMonitor(); |
| } |
| |
| } |