blob: 90da759f59ca4565e6969c1b08a840e6f3c9efeb [file] [log] [blame]
/*=============================================================================#
# 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 };
}
}