/******************************************************************************* * 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<>(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<>(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<>(); 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(); } }