| /*=============================================================================# |
| # Copyright (c) 2005, 2020 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.ltk.ui.sourceediting; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.core.commands.IHandler2; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IProjectNature; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.content.IContentType; |
| import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; |
| import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; |
| import org.eclipse.jface.action.IMenuManager; |
| import org.eclipse.jface.action.MenuManager; |
| import org.eclipse.jface.resource.ImageDescriptor; |
| import org.eclipse.jface.resource.JFaceResources; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.IInformationControl; |
| import org.eclipse.jface.text.IInformationControlCreator; |
| import org.eclipse.jface.text.IRegion; |
| import org.eclipse.jface.text.ISynchronizable; |
| import org.eclipse.jface.text.ITextSelection; |
| import org.eclipse.jface.text.ITextViewerExtension5; |
| import org.eclipse.jface.text.link.ILinkedModeListener; |
| import org.eclipse.jface.text.link.LinkedModeModel; |
| import org.eclipse.jface.text.source.IAnnotationModel; |
| import org.eclipse.jface.text.source.ISourceViewer; |
| import org.eclipse.jface.text.source.IVerticalRuler; |
| import org.eclipse.jface.text.source.SourceViewer; |
| import org.eclipse.jface.text.source.projection.IProjectionListener; |
| import org.eclipse.jface.text.source.projection.ProjectionSupport; |
| import org.eclipse.jface.text.source.projection.ProjectionViewer; |
| import org.eclipse.jface.viewers.IPostSelectionProvider; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.swt.custom.StyledText; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IFileEditorInput; |
| import org.eclipse.ui.IPageLayout; |
| import org.eclipse.ui.IWorkbenchPart; |
| import org.eclipse.ui.contexts.IContextService; |
| import org.eclipse.ui.editors.text.TextEditor; |
| import org.eclipse.ui.handlers.IHandlerService; |
| import org.eclipse.ui.part.IShowInSource; |
| import org.eclipse.ui.part.IShowInTargetList; |
| import org.eclipse.ui.part.ShowInContext; |
| import org.eclipse.ui.services.IServiceLocator; |
| 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.templates.ITemplatesPage; |
| import org.eclipse.ui.views.contentoutline.IContentOutlinePage; |
| |
| import org.eclipse.statet.jcommons.collections.CopyOnWriteIdentityListSet; |
| import org.eclipse.statet.jcommons.collections.ImCollections; |
| import org.eclipse.statet.jcommons.collections.ImIdentityList; |
| import org.eclipse.statet.jcommons.collections.ImList; |
| import org.eclipse.statet.jcommons.lang.Nullable; |
| import org.eclipse.statet.jcommons.text.core.BasicTextRegion; |
| import org.eclipse.statet.jcommons.text.core.TextRegion; |
| |
| import org.eclipse.statet.ecommons.preferences.PreferencesUtil; |
| import org.eclipse.statet.ecommons.preferences.SettingsChangeNotifier; |
| import org.eclipse.statet.ecommons.preferences.core.Preference; |
| import org.eclipse.statet.ecommons.preferences.core.util.PreferenceUtils; |
| import org.eclipse.statet.ecommons.text.ICharPairMatcher; |
| import org.eclipse.statet.ecommons.text.core.JFaceTextRegion; |
| import org.eclipse.statet.ecommons.text.core.sections.DocContentSections; |
| import org.eclipse.statet.ecommons.text.ui.TextHandlerUtil; |
| import org.eclipse.statet.ecommons.ui.ISettingsChangedHandler; |
| import org.eclipse.statet.ecommons.ui.util.UIAccess; |
| |
| import org.eclipse.statet.internal.ltk.ui.EditingMessages; |
| import org.eclipse.statet.ltk.ast.core.AstNode; |
| import org.eclipse.statet.ltk.core.Ltk; |
| import org.eclipse.statet.ltk.model.core.DocumentModelProvider; |
| import org.eclipse.statet.ltk.model.core.ModelManager; |
| import org.eclipse.statet.ltk.model.core.ModelTypeDescriptor; |
| import org.eclipse.statet.ltk.model.core.element.LtkModelElement; |
| import org.eclipse.statet.ltk.model.core.element.SourceStructElement; |
| import org.eclipse.statet.ltk.model.core.element.SourceUnit; |
| import org.eclipse.statet.ltk.model.core.element.SourceUnitModelInfo; |
| import org.eclipse.statet.ltk.ui.ElementInfoController; |
| import org.eclipse.statet.ltk.ui.ISelectionWithElementInfoListener; |
| import org.eclipse.statet.ltk.ui.LTKInputData; |
| import org.eclipse.statet.ltk.ui.LTKUI; |
| import org.eclipse.statet.ltk.ui.ModelElementInputProvider; |
| import org.eclipse.statet.ltk.ui.PostSelectionCancelExtension; |
| import org.eclipse.statet.ltk.ui.PostSelectionWithElementInfoController; |
| import org.eclipse.statet.ltk.ui.PostSelectionWithElementInfoController.IgnoreActivation; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.DeleteNextWordHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.DeletePreviousWordHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.GotoMatchingBracketHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.GotoNextWordHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.GotoPreviousWordHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.SelectNextWordHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.SelectPreviousWordHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.SpecificContentAssistHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.ToggleCommentHandler; |
| |
| |
| /** |
| * Abstract LTK based source editor. |
| */ |
| public abstract class SourceEditor1 extends TextEditor implements ISourceEditor, |
| SettingsChangeNotifier.ChangeListener, IPreferenceChangeListener, |
| IShowInSource, IShowInTargetList { |
| |
| |
| protected static final ImList<String> ACTION_SET_CONTEXT_IDS= ImCollections.newIdentityList( |
| "org.eclipse.statet.ltk.contexts.EditSource1MenuSet", //$NON-NLS-1$ |
| "org.eclipse.ui.edit.text.actionSet.presentation" ); //$NON-NLS-1$ |
| |
| private static final ImList<String> CONTEXT_IDS= ImCollections.addElement( |
| ACTION_SET_CONTEXT_IDS, |
| "org.eclipse.statet.workbench.contexts.TextEditor" ); //$NON-NLS-1$ |
| |
| |
| /*- Static utility methods --------------------------------------------------*/ |
| |
| @Deprecated |
| protected static IProjectNature getProject(final IEditorInput input, final String projectNatureId) { |
| if (input != null && input instanceof IFileEditorInput) { |
| final IProject project= ((IFileEditorInput) input).getFile().getProject(); |
| try { |
| if (project != null && project.hasNature(projectNatureId)) { |
| return project.getNature(projectNatureId); |
| } |
| } |
| catch (final CoreException ignore) {} |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the lock object for the given annotation model. |
| * |
| * @param annotationModel the annotation model |
| * @return the annotation model's lock object |
| */ |
| protected static final Object getLockObject(final IAnnotationModel annotationModel) { |
| if (annotationModel instanceof ISynchronizable) { |
| final Object lock= ((ISynchronizable) annotationModel).getLockObject(); |
| if (lock != null) { |
| return lock; |
| } |
| } |
| return annotationModel; |
| } |
| |
| private TextRegion expand(final TextRegion region, final @Nullable TextRegion regionOpt) { |
| return (regionOpt != null) ? region.expansion(regionOpt) : region; |
| } |
| |
| |
| /*- Inner classes -----------------------------------------------------------*/ |
| |
| protected class PostSelectionEditorCancel extends PostSelectionCancelExtension { |
| |
| public PostSelectionEditorCancel() { |
| } |
| |
| @Override |
| public void init() { |
| final ISourceViewer viewer= getSourceViewer(); |
| if (viewer != null) { |
| viewer.addTextInputListener(this); |
| viewer.getDocument().addDocumentListener(this); |
| } |
| } |
| |
| @Override |
| public void dispose() { |
| final ISourceViewer viewer= getSourceViewer(); |
| if (viewer != null) { |
| viewer.removeTextInputListener(this); |
| final IDocument document= viewer.getDocument(); |
| if (document != null) { |
| document.removeDocumentListener(this); |
| } |
| } |
| } |
| } |
| |
| private class EffectSynchonizer implements ITextEditToolSynchronizer, ILinkedModeListener { |
| |
| private EffectSynchonizer() { |
| } |
| |
| @Override |
| public void install(final LinkedModeModel model) { |
| SourceEditor1.this.effectSynchonizerCounter++; |
| if (SourceEditor1.this.markOccurrencesProvider != null) { |
| SourceEditor1.this.markOccurrencesProvider.uninstall(); |
| } |
| model.addLinkingListener(this); |
| } |
| |
| @Override |
| public void left(final LinkedModeModel model, final int flags) { |
| SourceEditor1.this.effectSynchonizerCounter--; |
| updateMarkOccurrencesEnablement(); |
| } |
| |
| @Override |
| public void resume(final LinkedModeModel model, final int flags) { |
| } |
| |
| @Override |
| public void suspend(final LinkedModeModel model) { |
| } |
| |
| } |
| |
| |
| /*- Fields -----------------------------------------------------------------*/ |
| |
| private final IContentType contentType; |
| private final ModelTypeDescriptor modelType; |
| |
| private SourceEditorViewerConfigurator configurator; |
| private boolean lazySetup; |
| private SourceUnit sourceUnit; |
| private ElementInfoController modelProvider; |
| private PostSelectionWithElementInfoController modelPostSelection; |
| protected volatile Point currentSelection; |
| |
| /** The outline page of this editor */ |
| private SourceEditor1OutlinePage outlinePage; |
| |
| /** The templates page of this editor */ |
| private ITemplatesPage templatesPage; |
| |
| private StructureSelectionHistory selectionHistory; |
| private Preference<Boolean> foldingEnablement; |
| private ProjectionSupport foldingSupport; |
| private ISourceEditorAddon foldingProvider; |
| private FoldingActionGroup foldingActionGroup; |
| private Preference<Boolean> markOccurrencesEnablement; |
| private ISourceEditorAddon markOccurrencesProvider; |
| |
| private EffectSynchonizer effectSynchronizer; |
| private int effectSynchonizerCounter; |
| |
| private final CopyOnWriteIdentityListSet<IUpdate> contentUpdateables= new CopyOnWriteIdentityListSet<>(); |
| private final CopyOnWriteIdentityListSet<IHandler2> stateUpdateables= new CopyOnWriteIdentityListSet<>(); |
| |
| private boolean inputChange; |
| private int inputUpdate= Integer.MAX_VALUE; |
| |
| private ImageDescriptor imageDescriptor; |
| |
| |
| /*- Contructors ------------------------------------------------------------*/ |
| |
| public SourceEditor1(final IContentType contentType) { |
| super(); |
| this.contentType= contentType; |
| this.modelType= Ltk.getExtContentTypeManager().getModelTypeForContentType(contentType.getId()); |
| } |
| |
| |
| /*- Methods ----------------------------------------------------------------*/ |
| |
| @Override |
| public IContentType getContentType() { |
| return this.contentType; |
| } |
| |
| /** |
| * Returns the model type of source units of the editor. |
| * The value must not change for an editor instance and all source units |
| * in the editor must be of the same type. |
| * |
| * @return id of the model type |
| */ |
| public String getModelTypeId() { |
| return this.modelType.getId(); |
| } |
| |
| @Override |
| protected void initializeEditor() { |
| this.configurator= createConfiguration(); |
| super.initializeEditor(); |
| setCompatibilityMode(false); |
| final SourceEditorViewerConfiguration configuration= this.configurator.getSourceViewerConfiguration(); |
| setPreferenceStore(configuration.getPreferences()); |
| setSourceViewerConfiguration(configuration); |
| if (configuration.isSmartInsertSupported()) { |
| configureInsertMode(SMART_INSERT, true); |
| } |
| |
| PreferencesUtil.getSettingsChangeNotifier().addChangeListener(this); |
| } |
| |
| protected abstract SourceEditorViewerConfigurator createConfiguration(); |
| |
| protected SourceEditorViewerConfigurator createInfoConfigurator() { |
| return null; |
| } |
| |
| |
| protected void enableStructuralFeatures(final ModelManager modelManager, |
| final Preference<Boolean> codeFoldingEnablement, |
| final Preference<Boolean> markOccurrencesEnablement) { |
| this.modelProvider= new ElementInfoController(modelManager, Ltk.EDITOR_CONTEXT); |
| this.foldingEnablement= codeFoldingEnablement; |
| this.markOccurrencesEnablement= markOccurrencesEnablement; |
| } |
| |
| /** |
| * Overwrites the default title image (editor icon) during the initialization of the editor |
| * input. |
| * |
| * The image is created and disposed automatically. |
| * |
| * For example, it can be used to overwrite the default image using the image descriptor of |
| * the editor input in {@link #setDocumentProvider(IEditorInput)} |
| * |
| * @param descriptor the image description of the icon or <code>null</code> |
| */ |
| protected void overwriteTitleImage(final ImageDescriptor descriptor) { |
| if (this.imageDescriptor == descriptor || (this.imageDescriptor == null && descriptor == null) ) { |
| return; |
| } |
| if (this.imageDescriptor != null) { |
| JFaceResources.getResources().destroyImage(this.imageDescriptor); |
| } |
| this.imageDescriptor= descriptor; |
| if (this.imageDescriptor != null) { |
| super.setTitleImage(JFaceResources.getResources().createImage(descriptor)); |
| } |
| } |
| |
| @Override |
| protected void setTitleImage(final Image titleImage) { |
| if (this.imageDescriptor == null) { |
| super.setTitleImage(titleImage); |
| } |
| } |
| |
| protected Collection<String> getContextIds() { |
| return CONTEXT_IDS; |
| } |
| |
| @Override |
| protected void initializeKeyBindingScopes() { |
| final Collection<String> ids= getContextIds(); |
| setKeyBindingScopes(ids.toArray(new String[ids.size()])); |
| } |
| |
| @Override |
| protected String[] collectContextMenuPreferencePages() { |
| final List<String> list= new ArrayList<>(); |
| collectContextMenuPreferencePages(list); |
| list.addAll(Arrays.asList(super.collectContextMenuPreferencePages())); |
| return list.toArray(new String[list.size()]); |
| } |
| |
| protected void collectContextMenuPreferencePages(final List<String> pageIds) { |
| } |
| |
| |
| @Override |
| protected void doSetInput(final IEditorInput input) throws CoreException { |
| if (this.modelProvider != null && this.sourceUnit != null) { |
| this.modelProvider.setInput(null); |
| } |
| |
| // project has changed |
| final ISourceViewer sourceViewer= getSourceViewer(); |
| if (sourceViewer != null) { |
| this.configurator.unconfigureTarget(); |
| } |
| else { |
| this.lazySetup= true; |
| } |
| |
| this.inputChange= true; |
| this.inputUpdate= 1; |
| super.doSetInput(input); |
| // setup in |
| // 1) setDocumentProvider -> setupConfiguration(..., input) |
| // 2) handleInsertModeChanged -> setupConfiguration(..., input, SourceViewer) |
| this.inputChange= false; |
| this.inputUpdate= Integer.MAX_VALUE; |
| |
| initSmartInsert(); |
| |
| if (input != null && this.outlinePage != null) { |
| updateOutlinePageInput(this.outlinePage); |
| } |
| } |
| |
| private void initSmartInsert() { |
| final SourceEditorViewerConfiguration config= this.configurator.getSourceViewerConfiguration(); |
| if (config.isSmartInsertSupported()) { |
| if (config.isSmartInsertByDefault()) { |
| setInsertMode(SMART_INSERT); |
| } |
| else { |
| setInsertMode(INSERT); |
| } |
| } |
| } |
| |
| @Override |
| protected void setPartName(final String partName) { |
| super.setPartName(partName); |
| |
| // see doSetInput |
| if (this.inputChange) { |
| if (this.inputUpdate != 1) { |
| return; |
| } |
| this.inputUpdate= 2; |
| final IEditorInput input= getEditorInput(); |
| setupConfiguration(input); |
| } |
| } |
| |
| @Override |
| protected void handleInsertModeChanged() { |
| // see doSetInput |
| if (this.inputChange && !this.lazySetup) { |
| if (this.inputUpdate != 2) { |
| return; |
| } |
| this.inputUpdate= 3; |
| final IEditorInput input= getEditorInput(); |
| final ISourceViewer sourceViewer= getSourceViewer(); |
| if (input != null && sourceViewer != null) { |
| setupConfiguration(input, sourceViewer); |
| this.configurator.configureTarget(); |
| } |
| this.inputChange= false; |
| } |
| |
| super.handleInsertModeChanged(); |
| } |
| |
| /** |
| * Subclasses should setup the SourceViewerConfiguration. |
| */ |
| protected void setupConfiguration(final IEditorInput newInput) { |
| final IDocumentProvider documentProvider= getDocumentProvider(); |
| if (documentProvider instanceof DocumentModelProvider) { |
| this.sourceUnit= (newInput != null) ? |
| ((DocumentModelProvider)documentProvider).getWorkingCopy(newInput) : |
| null; |
| if (this.modelProvider != null) { |
| this.modelProvider.setInput(this.sourceUnit); |
| } |
| } |
| } |
| |
| /** |
| * Subclasses should setup the SourceViewerConfiguration. |
| */ |
| protected void setupConfiguration(final IEditorInput newInput, final ISourceViewer sourceViewer) { |
| updateStateDependentActions(); |
| } |
| |
| @Override |
| public SourceUnit getSourceUnit() { |
| return this.sourceUnit; |
| } |
| |
| |
| @Override |
| public SourceViewer getViewer() { |
| return (SourceViewer) super.getSourceViewer(); |
| } |
| |
| @Override |
| public DocContentSections getDocumentContentInfo() { |
| return this.configurator.getDocumentContentInfo(); |
| } |
| |
| @Override |
| public IWorkbenchPart getWorkbenchPart() { |
| return this; |
| } |
| |
| @Override |
| public IServiceLocator getServiceLocator() { |
| return getSite(); |
| } |
| |
| @Override |
| public boolean isEditable(final boolean validate) { |
| if (validate) { |
| return SourceEditor1.this.validateEditorInputState(); |
| } |
| return SourceEditor1.this.isEditorInputModifiable(); |
| } |
| |
| public ModelElementInputProvider getModelInputProvider() { |
| return this.modelProvider; |
| } |
| |
| public void addPostSelectionWithElementInfoListener(final ISelectionWithElementInfoListener listener) { |
| if (this.modelPostSelection != null) { |
| this.modelPostSelection.addListener(listener); |
| } |
| } |
| |
| public void removePostSelectionWithElementInfoListener(final ISelectionWithElementInfoListener listener) { |
| if (this.modelPostSelection != null) { |
| this.modelPostSelection.removeListener(listener); |
| } |
| } |
| |
| |
| @Override |
| public void createPartControl(final Composite parent) { |
| super.createPartControl(parent); |
| |
| if (this.modelProvider != null) { |
| this.modelPostSelection= new PostSelectionWithElementInfoController(this.modelProvider, |
| (IPostSelectionProvider) getSelectionProvider(), new PostSelectionEditorCancel()); |
| this.modelPostSelection.addListener(new ISelectionWithElementInfoListener() { |
| @Override |
| public void inputChanged() { |
| } |
| @Override |
| public void stateChanged(final LTKInputData state) { |
| final TextRegion toHighlight= getRangeToHighlight(state); |
| if (toHighlight != null) { |
| setHighlightRange(toHighlight.getStartOffset(), toHighlight.getLength(), false); |
| } |
| else { |
| resetHighlightRange(); |
| } |
| } |
| }); |
| } |
| if (this.foldingEnablement != null) { |
| final ProjectionViewer viewer= (ProjectionViewer) getSourceViewer(); |
| |
| this.foldingSupport= new ProjectionSupport(viewer, getAnnotationAccess(), getSharedColors()); |
| final SourceEditorViewerConfigurator config= createInfoConfigurator(); |
| if (config != null) { |
| final IInformationControlCreator presentationCreator= new IInformationControlCreator() { |
| @Override |
| public IInformationControl createInformationControl(final Shell parent) { |
| return new SourceViewerInformationControl(parent, createInfoConfigurator(), getOrientation()); |
| } |
| }; |
| this.foldingSupport.setHoverControlCreator(new IInformationControlCreator() { |
| @Override |
| public IInformationControl createInformationControl(final Shell parent) { |
| return new SourceViewerInformationControl(parent, createInfoConfigurator(), getOrientation(), presentationCreator); |
| } |
| }); |
| this.foldingSupport.setInformationPresenterControlCreator(presentationCreator); |
| } |
| this.foldingSupport.install(); |
| viewer.addProjectionListener(new IProjectionListener() { |
| @Override |
| public void projectionEnabled() { |
| installFoldingProvider(); |
| } |
| @Override |
| public void projectionDisabled() { |
| uninstallFoldingProvider(); |
| } |
| }); |
| PreferenceUtils.getInstancePrefs().addPreferenceNodeListener( |
| this.foldingEnablement.getQualifier(), this); |
| updateFoldingEnablement(); |
| } |
| if (this.markOccurrencesEnablement != null) { |
| PreferenceUtils.getInstancePrefs().addPreferenceNodeListener( |
| this.markOccurrencesEnablement.getQualifier(), this); |
| updateMarkOccurrencesEnablement(); |
| } |
| |
| if (this.lazySetup) { |
| this.lazySetup= false; |
| setupConfiguration(getEditorInput(), getSourceViewer()); |
| this.configurator.setTarget(this); |
| } |
| |
| { final IContextService contextService= getServiceLocator().getService(IContextService.class); |
| for (final String id : getContextIds()) { |
| contextService.activateContext(id); |
| } |
| } |
| } |
| |
| @Override |
| protected ISourceViewer createSourceViewer(final Composite parent, final IVerticalRuler ruler, final int styles) { |
| this.fAnnotationAccess= getAnnotationAccess(); |
| this.fOverviewRuler= createOverviewRuler(getSharedColors()); |
| |
| final ISourceViewer viewer= new SourceEditorViewer(parent, |
| ruler, getOverviewRuler(), isOverviewRulerVisible(), styles, |
| getSourceViewerFlags() ); |
| // ensure decoration support has been created and configured. |
| getSourceViewerDecorationSupport(viewer); |
| |
| return viewer; |
| } |
| |
| protected int getSourceViewerFlags() { |
| return 0; |
| } |
| |
| protected IRegion getRangeToReveal(final SourceUnitModelInfo modelInfo, final SourceStructElement element) { |
| return null; |
| } |
| |
| protected TextRegion getRangeToHighlight(final LTKInputData state) { |
| final SourceUnitModelInfo info= state.getInputInfo(); |
| if (info == null) { |
| return null; |
| } |
| |
| final TextRegion region= getRangeToHighlight(info, state.getModelSelection()); |
| if (region != null) { |
| return region; |
| } |
| |
| final AstNode root= info.getAst().getRoot(); |
| TRY_AST: if (root != null) { |
| final ITextSelection selection= (ITextSelection) state.getSelection(); |
| final int n= root.getChildCount(); |
| for (int i= 0; i < n; i++) { |
| final AstNode child= root.getChild(i); |
| if (selection.getOffset() >= child.getStartOffset()) { |
| if (selection.getOffset()+selection.getLength() <= child.getEndOffset()) { |
| return child; |
| } |
| } |
| else { |
| break TRY_AST; |
| } |
| } |
| } |
| return null; |
| } |
| |
| protected TextRegion getRangeToHighlight(final SourceUnitModelInfo info, SourceStructElement element) { |
| while (element != null) { |
| switch (element.getElementType() & LtkModelElement.MASK_C1) { |
| case LtkModelElement.C1_CLASS: |
| case LtkModelElement.C1_METHOD: |
| return expand(element.getSourceRange(), element.getDocumentationRange()); |
| case LtkModelElement.C1_SOURCE: |
| if ((element.getElementType() & LtkModelElement.MASK_C2) == LtkModelElement.C2_SOURCE_CHUNK) { |
| return expand(element.getSourceRange(), element.getDocumentationRange()); |
| } |
| return null; |
| case LtkModelElement.C1_VARIABLE: |
| if ((element.getSourceParent().getElementType() & LtkModelElement.MASK_C2) == LtkModelElement.C2_SOURCE_FILE) { |
| return expand(element.getSourceRange(), element.getDocumentationRange()); |
| } |
| //$FALL-THROUGH$ |
| default: |
| element= element.getSourceParent(); |
| continue; |
| } |
| } |
| return null; |
| } |
| |
| |
| protected ISourceEditorAddon createCodeFoldingProvider() { |
| return null; |
| } |
| |
| private void installFoldingProvider() { |
| uninstallFoldingProvider(); |
| this.foldingProvider= createCodeFoldingProvider(); |
| if (this.foldingProvider != null) { |
| this.foldingProvider.install(this); |
| } |
| } |
| |
| private void uninstallFoldingProvider() { |
| if (this.foldingProvider != null) { |
| this.foldingProvider.uninstall(); |
| this.foldingProvider= null; |
| } |
| } |
| |
| private void updateFoldingEnablement() { |
| if (this.foldingEnablement != null) { |
| UIAccess.getDisplay().asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| final Boolean enable= PreferenceUtils.getInstancePrefs().getPreferenceValue(SourceEditor1.this.foldingEnablement); |
| final ProjectionViewer viewer= (ProjectionViewer) getSourceViewer(); |
| if (enable != null && UIAccess.isOkToUse(viewer)) { |
| if (enable != viewer.isProjectionMode()) { |
| viewer.doOperation(ProjectionViewer.TOGGLE); |
| } |
| } |
| } |
| }); |
| } |
| } |
| |
| |
| protected ISourceEditorAddon createMarkOccurrencesProvider() { |
| return null; |
| } |
| |
| private void uninstallMarkOccurrencesProvider() { |
| if (this.markOccurrencesProvider != null) { |
| this.markOccurrencesProvider.uninstall(); |
| this.markOccurrencesProvider= null; |
| } |
| } |
| |
| private void updateMarkOccurrencesEnablement() { |
| if (this.markOccurrencesEnablement != null) { |
| UIAccess.getDisplay().asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| final Boolean enable= PreferenceUtils.getInstancePrefs().getPreferenceValue(SourceEditor1.this.markOccurrencesEnablement); |
| if (enable) { |
| if (SourceEditor1.this.markOccurrencesProvider == null) { |
| SourceEditor1.this.markOccurrencesProvider= createMarkOccurrencesProvider(); |
| } |
| if (SourceEditor1.this.markOccurrencesProvider != null && SourceEditor1.this.effectSynchonizerCounter == 0) { |
| SourceEditor1.this.markOccurrencesProvider.install(SourceEditor1.this); |
| } |
| } |
| else { |
| uninstallMarkOccurrencesProvider(); |
| } |
| } |
| }); |
| } |
| } |
| |
| |
| @Override |
| protected void configureSourceViewerDecorationSupport(final SourceViewerDecorationSupport support) { |
| super.configureSourceViewerDecorationSupport(support); |
| this.configurator.configureSourceViewerDecorationSupport(support); |
| } |
| |
| @Override |
| protected void createActions() { |
| super.createActions(); |
| final IHandlerService handlerService= getServiceLocator().getService(IHandlerService.class); |
| final StyledText textWidget= getViewer().getTextWidget(); |
| |
| { final IHandler2 handler= new GotoNextWordHandler(this); |
| setAction(ITextEditorActionDefinitionIds.WORD_NEXT, null); |
| TextHandlerUtil.disable(textWidget, ITextEditorActionDefinitionIds.WORD_NEXT); |
| handlerService.activateHandler(ITextEditorActionDefinitionIds.WORD_NEXT, handler); |
| } |
| { final IHandler2 handler= new GotoPreviousWordHandler(this); |
| setAction(ITextEditorActionDefinitionIds.WORD_PREVIOUS, null); |
| TextHandlerUtil.disable(textWidget, ITextEditorActionDefinitionIds.WORD_NEXT); |
| handlerService.activateHandler(ITextEditorActionDefinitionIds.WORD_PREVIOUS, handler); |
| } |
| { final IHandler2 handler= new SelectNextWordHandler(this); |
| setAction(ITextEditorActionDefinitionIds.SELECT_WORD_NEXT, null); |
| TextHandlerUtil.disable(textWidget, ITextEditorActionDefinitionIds.SELECT_WORD_NEXT); |
| handlerService.activateHandler(ITextEditorActionDefinitionIds.SELECT_WORD_NEXT, handler); |
| } |
| { final IHandler2 handler= new SelectPreviousWordHandler(this); |
| setAction(ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS, null); |
| TextHandlerUtil.disable(textWidget, ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS); |
| handlerService.activateHandler(ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS, handler); |
| } |
| { final IHandler2 handler= new DeleteNextWordHandler(this); |
| setAction(ITextEditorActionDefinitionIds.DELETE_NEXT_WORD, null); |
| TextHandlerUtil.disable(textWidget, ITextEditorActionDefinitionIds.DELETE_NEXT_WORD); |
| handlerService.activateHandler(ITextEditorActionDefinitionIds.DELETE_NEXT_WORD, handler); |
| markAsStateDependentHandler(handler, true); |
| } |
| { final IHandler2 handler= new DeletePreviousWordHandler(this); |
| setAction(ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD, null); |
| TextHandlerUtil.disable(textWidget, ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD); |
| handlerService.activateHandler(ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD, handler); |
| markAsStateDependentHandler(handler, true); |
| } |
| |
| final ICharPairMatcher matcher= this.configurator.getSourceViewerConfiguration().getPairMatcher(); |
| if (matcher != null) { |
| handlerService.activateHandler(ISourceEditorCommandIds.GOTO_MATCHING_BRACKET, |
| new GotoMatchingBracketHandler(matcher, this)); |
| } |
| { final IHandler2 handler= new SpecificContentAssistHandler(this, |
| this.configurator.getSourceViewerConfiguration().getContentAssist() ); |
| handlerService.activateHandler(ISourceEditorCommandIds.SPECIFIC_CONTENT_ASSIST_COMMAND_ID, handler); |
| } |
| |
| { final IHandler2 handler= createToggleCommentHandler(); |
| if (handler != null) { |
| handlerService.activateHandler(ISourceEditorCommandIds.TOGGLE_COMMENT, handler); |
| } |
| } |
| { final IHandler2 handler= createCorrectIndentHandler(); |
| if (handler != null) { |
| handlerService.activateHandler(LTKUI.CORRECT_INDENT_COMMAND_ID, handler); |
| } |
| } |
| |
| if (this.foldingEnablement != null) { |
| this.foldingActionGroup= createFoldingActionGroup(); |
| } |
| if (this.modelProvider != null) { |
| this.selectionHistory= new StructureSelectionHistory(this); |
| handlerService.activateHandler(ISourceEditorCommandIds.SELECT_ENCLOSING, |
| new StructureSelectHandler.Enclosing(this, this.selectionHistory)); |
| handlerService.activateHandler(ISourceEditorCommandIds.SELECT_PREVIOUS, |
| new StructureSelectHandler.Previous(this, this.selectionHistory)); |
| handlerService.activateHandler(ISourceEditorCommandIds.SELECT_NEXT, |
| new StructureSelectHandler.Next(this, this.selectionHistory)); |
| final StructureSelectionHistoryBackHandler backHandler= new StructureSelectionHistoryBackHandler(this, this.selectionHistory); |
| handlerService.activateHandler(ISourceEditorCommandIds.SELECT_LAST, backHandler); |
| this.selectionHistory.addUpdateListener(backHandler); |
| } |
| |
| //WorkbenchHelp.setHelp(action, IJavaHelpContextIds.TOGGLE_COMMENT_ACTION); |
| } |
| |
| protected FoldingActionGroup createFoldingActionGroup() { |
| return new FoldingActionGroup(this, (ProjectionViewer) getSourceViewer()); |
| } |
| |
| protected IHandler2 createToggleCommentHandler() { |
| final IHandler2 commentHandler= new ToggleCommentHandler(this); |
| markAsStateDependentHandler(commentHandler, true); |
| return commentHandler; |
| } |
| |
| protected IHandler2 createCorrectIndentHandler() { |
| return null; |
| } |
| |
| protected void markAsContentDependentHandler(final IUpdate handler, final boolean mark) { |
| if (mark) { |
| this.contentUpdateables.add(handler); |
| } |
| else { |
| this.contentUpdateables.remove(handler); |
| } |
| } |
| |
| protected void markAsStateDependentHandler(final IHandler2 handler, final boolean mark) { |
| if (mark) { |
| this.stateUpdateables.add(handler); |
| } |
| else { |
| this.stateUpdateables.remove(handler); |
| } |
| } |
| |
| @Override |
| protected void updateContentDependentActions() { |
| super.updateContentDependentActions(); |
| for (final IUpdate handler : this.contentUpdateables.toList()) { |
| handler.update(); |
| } |
| } |
| |
| @Override |
| protected void updateStateDependentActions() { |
| super.updateStateDependentActions(); |
| final ImIdentityList<IHandler2> listeners= this.stateUpdateables.toList(); |
| for (final IHandler2 handler : listeners) { |
| handler.setEnabled(this); |
| } |
| } |
| |
| @Override |
| protected void rulerContextMenuAboutToShow(final IMenuManager menu) { |
| super.rulerContextMenuAboutToShow(menu); |
| if (this.foldingActionGroup != null) { |
| final IMenuManager foldingMenu= new MenuManager(EditingMessages.CodeFolding_label, "projection"); //$NON-NLS-1$ |
| menu.appendToGroup(ITextEditorActionConstants.GROUP_RULERS, foldingMenu); |
| this.foldingActionGroup.fillMenu(foldingMenu); |
| } |
| } |
| |
| |
| @Override |
| public ITextEditToolSynchronizer getTextEditToolSynchronizer() { |
| if (this.effectSynchronizer == null) { |
| this.effectSynchronizer= new EffectSynchonizer(); |
| } |
| return this.effectSynchronizer; |
| } |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public <T> @Nullable T getAdapter(final Class<T> adapterType) { |
| if (adapterType == ISourceEditor.class) { |
| return (T) this; |
| } |
| if (adapterType == ISourceViewer.class) { |
| return (T) getSourceViewer(); |
| } |
| |
| if (adapterType == IContentType.class) { |
| return (T) this.contentType; |
| } |
| |
| if (adapterType == IContentOutlinePage.class) { |
| if (this.outlinePage == null) { |
| this.outlinePage= createOutlinePage(); |
| if (this.outlinePage != null) { |
| updateOutlinePageInput(this.outlinePage); |
| } |
| } |
| return (T) this.outlinePage; |
| } |
| if (adapterType == ITemplatesPage.class) { |
| if (this.templatesPage == null) { |
| this.templatesPage= createTemplatesPage(); |
| } |
| return (T) this.templatesPage; |
| } |
| if (this.foldingSupport != null) { |
| final Object adapter= this.foldingSupport.getAdapter(getSourceViewer(), adapterType); |
| if (adapter != null) { |
| return (T) adapter; |
| } |
| } |
| |
| return super.getAdapter(adapterType); |
| } |
| |
| |
| @Override |
| public void settingsChanged(final Set<String> groupIds) { |
| final Map<String, Object> options= new HashMap<>(); |
| UIAccess.getDisplay().syncExec(new Runnable() { |
| @Override |
| public void run() { |
| handleSettingsChanged(groupIds, options); |
| } |
| }); |
| } |
| |
| /** |
| * @see ISettingsChangedHandler#handleSettingsChanged(Set, Map) |
| */ |
| protected void handleSettingsChanged(final Set<String> groupIds, final Map<String, Object> options) { |
| if (this.configurator != null) { |
| this.configurator.handleSettingsChanged(groupIds, options); |
| } |
| } |
| |
| @Override |
| public void preferenceChange(final PreferenceChangeEvent event) { |
| if (this.foldingEnablement != null && event.getKey().equals(this.foldingEnablement.getKey())) { |
| updateFoldingEnablement(); |
| } |
| if (this.markOccurrencesEnablement != null && event.getKey().equals(this.markOccurrencesEnablement.getKey())) { |
| updateMarkOccurrencesEnablement(); |
| } |
| } |
| |
| protected void updateIndentSettings() { |
| updateIndentPrefixes(); |
| } |
| |
| |
| @Override |
| protected void handleCursorPositionChanged() { |
| this.currentSelection= getSourceViewer().getSelectedRange(); |
| super.handleCursorPositionChanged(); |
| } |
| |
| |
| protected SourceEditor1OutlinePage createOutlinePage() { |
| return null; |
| } |
| |
| protected void updateOutlinePageInput(final SourceEditor1OutlinePage page) { |
| } |
| |
| void handleOutlinePageClosed() { |
| if (this.outlinePage != null) { |
| this.outlinePage= null; |
| resetHighlightRange(); |
| } |
| } |
| |
| protected ITemplatesPage createTemplatesPage() { |
| return null; |
| } |
| |
| |
| @Override |
| // inject annotation painter workaround |
| protected SourceViewerDecorationSupport getSourceViewerDecorationSupport(final ISourceViewer viewer) { |
| if (this.fSourceViewerDecorationSupport == null) { |
| this.fSourceViewerDecorationSupport= new org.eclipse.statet.ltk.ui.sourceediting.SourceViewerDecorationSupport(viewer, getOverviewRuler(), getAnnotationAccess(), getSharedColors()); |
| configureSourceViewerDecorationSupport(this.fSourceViewerDecorationSupport); |
| } |
| return this.fSourceViewerDecorationSupport; |
| } |
| |
| @Override |
| public void selectAndReveal(final int start, final int length) { |
| selectAndReveal(start, length, start, length); |
| } |
| |
| @Override |
| protected void selectAndReveal(final int selectionStart, final int selectionLength, final int revealStart, final int revealLength) { |
| if (this.modelPostSelection != null) { |
| this.modelPostSelection.setUpdateOnSelection(true); |
| try { |
| super.selectAndReveal(selectionStart, selectionLength, revealStart, revealLength); |
| } |
| finally { |
| this.modelPostSelection.setUpdateOnSelection(false); |
| } |
| } |
| else { |
| super.selectAndReveal(selectionStart, selectionLength, revealStart, revealLength); |
| } |
| } |
| |
| public void setSelection(final ISelection selection, final ISelectionWithElementInfoListener listener) { |
| if (this.modelPostSelection != null && listener != null) { |
| final IgnoreActivation activation= this.modelPostSelection.ignoreNext(listener); |
| doSetSelection(selection); |
| activation.deleteNext(); |
| } |
| else { |
| doSetSelection(selection); |
| } |
| } |
| |
| @Override |
| protected void doSetSelection(final ISelection selection) { |
| if (selection instanceof IStructuredSelection) { |
| final IStructuredSelection structured= (IStructuredSelection) selection; |
| if (!structured.isEmpty()) { |
| final Object first= structured.getFirstElement(); |
| TextRegion region= null; |
| if (first instanceof SourceStructElement) { |
| final SourceStructElement sourceElement= (SourceStructElement) first; |
| region= sourceElement.getNameSourceRange(); |
| if (region == null) { |
| region= sourceElement.getSourceRange(); |
| if (region != null) { |
| region= new BasicTextRegion(region.getStartOffset()); |
| } |
| } |
| |
| final SourceUnit sourceUnit= sourceElement.getSourceUnit(); |
| final SourceUnitModelInfo modelInfo= sourceUnit.getModelInfo(getModelTypeId(), 0, null); |
| if (modelInfo != null) { |
| final IRegion toReveal= getRangeToReveal(modelInfo, sourceElement); |
| if (toReveal != null) { |
| final SourceViewer viewer= getViewer(); |
| if (viewer instanceof ITextViewerExtension5) { |
| ((ITextViewerExtension5) viewer).exposeModelRange(toReveal); |
| } |
| getViewer().revealRange(toReveal.getOffset(), toReveal.getLength()); |
| } |
| final TextRegion toHighlight= getRangeToHighlight(modelInfo, sourceElement); |
| if (toHighlight != null) { |
| setHighlightRange(toHighlight.getStartOffset(), toHighlight.getLength(), true); |
| } |
| } |
| } |
| if (region == null && first instanceof TextRegion) { |
| region= (TextRegion) first; |
| } |
| else if (region == null && first instanceof IRegion) { |
| region= JFaceTextRegion.toTextRegion((IRegion) first); |
| } |
| |
| if (region != null) { |
| selectAndReveal(region.getStartOffset(), region.getLength()); |
| return; |
| } |
| } |
| } |
| super.doSetSelection(selection); |
| } |
| |
| |
| @Override |
| public void dispose() { |
| if (this.modelProvider != null) { |
| this.modelProvider.setInput(null); |
| this.modelProvider.dispose(); |
| } |
| |
| PreferencesUtil.getSettingsChangeNotifier().removeChangeListener(this); |
| if (this.modelPostSelection != null) { |
| this.modelPostSelection.dispose(); |
| } |
| if (this.foldingEnablement != null) { |
| PreferenceUtils.getInstancePrefs().removePreferenceNodeListener( |
| this.foldingEnablement.getQualifier(), this); |
| uninstallFoldingProvider(); |
| } |
| if (this.markOccurrencesEnablement != null) { |
| PreferenceUtils.getInstancePrefs().removePreferenceNodeListener( |
| this.markOccurrencesEnablement.getQualifier(), this); |
| uninstallMarkOccurrencesProvider(); |
| } |
| |
| super.dispose(); |
| |
| if (this.imageDescriptor != null) { |
| JFaceResources.getResources().destroyImage(this.imageDescriptor); |
| this.imageDescriptor= null; |
| } |
| |
| this.sourceUnit= null; |
| this.modelProvider= null; |
| this.modelPostSelection= null; |
| } |
| |
| @Override |
| public ShowInContext getShowInContext() { |
| final Point selectionPoint= this.currentSelection; |
| final ISourceViewer sourceViewer= getSourceViewer(); |
| final SourceUnit unit= getSourceUnit(); |
| ISelection selection= null; |
| if (selectionPoint != null && unit != null && sourceViewer != null) { |
| selection= new LTKInputData(unit, getSelectionProvider()); |
| } |
| return new ShowInContext(getEditorInput(), selection); |
| } |
| |
| @Override |
| public String[] getShowInTargetIds() { |
| return new String[] { IPageLayout.ID_PROJECT_EXPLORER }; |
| } |
| |
| } |