blob: 580c037c38c62732fdddba206d716be1ced6508b [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2005, 2021 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 static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
import java.util.ArrayList;
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.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.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.ImList;
import org.eclipse.statet.jcommons.lang.NonNull;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
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.LTKInputData;
import org.eclipse.statet.ltk.ui.LtkActions;
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.SelectionWithElementInfoListener;
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;
import org.eclipse.statet.ltk.ui.util.LTKSelectionUtils;
/**
* Abstract LTK based source editor.
*/
@NonNullByDefault
public abstract class SourceEditor1 extends TextEditor implements SourceEditor,
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 --------------------------------------------------*/
/**
* 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 TextEditToolSynchronizer, ILinkedModeListener {
private EffectSynchonizer() {
}
@Override
public void install(final LinkedModeModel model) {
SourceEditor1.this.effectSynchonizerCounter++;
final var markOccurrencesProvider= SourceEditor1.this.markOccurrencesProvider;
if (markOccurrencesProvider != null) {
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 @Nullable SourceUnit sourceUnit;
private ElementInfoController modelProvider;
private @Nullable PostSelectionWithElementInfoController modelPostSelection;
protected volatile Point currentSelection;
/** The outline page of this editor */
private @Nullable SourceEditor1OutlinePage outlinePage;
/** The templates page of this editor */
private @Nullable ITemplatesPage templatesPage;
private StructureSelectionHistory selectionHistory;
private @Nullable Preference<Boolean> foldingEnablement;
private ProjectionSupport foldingSupport;
private @Nullable SourceEditorAddon foldingProvider;
private @Nullable FoldingActionGroup foldingActionGroup;
private @Nullable Preference<Boolean> markOccurrencesEnablement;
private @Nullable SourceEditorAddon markOccurrencesProvider;
private @Nullable 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 @Nullable 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 @Nullable SourceEditorViewerConfigurator createInfoConfigurator() {
return null;
}
protected void enableStructuralFeatures(final ModelManager modelManager,
final @Nullable Preference<Boolean> codeFoldingEnablement,
final @Nullable 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 @Nullable ImageDescriptor descriptor) {
final var oldDescriptor= this.imageDescriptor;
if (oldDescriptor == descriptor) {
return;
}
if (oldDescriptor != null) {
JFaceResources.getResources().destroyImage(oldDescriptor);
}
this.imageDescriptor= descriptor;
if (descriptor != null) {
super.setTitleImage(JFaceResources.getResources().createImage(descriptor));
}
}
@Override
protected void setTitleImage(final @Nullable 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 @NonNull String[] collectContextMenuPreferencePages() {
final List<String> list= new ArrayList<>();
collectContextMenuPreferencePages(list);
list.addAll(ImCollections.newList(super.collectContextMenuPreferencePages()));
return list.toArray(new @NonNull String[list.size()]);
}
protected void collectContextMenuPreferencePages(final List<String> pageIds) {
}
@Override
protected void doSetInput(final @Nullable 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) {
final var outlinePage= this.outlinePage;
if (outlinePage != null) {
updateOutlinePageInput(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 @Nullable 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 @Nullable 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 SelectionWithElementInfoListener listener) {
final var modelPostSelection= this.modelPostSelection;
if (modelPostSelection != null) {
modelPostSelection.addListener(listener);
}
}
public void removePostSelectionWithElementInfoListener(final SelectionWithElementInfoListener listener) {
final var modelPostSelection= this.modelPostSelection;
if (modelPostSelection != null) {
modelPostSelection.removeListener(listener);
}
}
@Override
public void createPartControl(final Composite parent) {
super.createPartControl(parent);
if (this.modelProvider != null) {
final var modelPostSelection= new PostSelectionWithElementInfoController(
this.modelProvider, (IPostSelectionProvider)getSelectionProvider(),
new PostSelectionEditorCancel() );
modelPostSelection.addListener(new SelectionWithElementInfoListener() {
@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();
}
}
});
this.modelPostSelection= modelPostSelection;
}
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= nonNullAssert(
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 @Nullable IRegion getRangeToReveal(final SourceUnitModelInfo modelInfo,
final SourceStructElement<?, ?> element) {
return null;
}
protected @Nullable 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 @Nullable TextRegion getRangeToHighlight(final SourceUnitModelInfo info,
@Nullable 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 @Nullable SourceEditorAddon createCodeFoldingProvider() {
return null;
}
private void installFoldingProvider() {
uninstallFoldingProvider();
final var foldingProvider= createCodeFoldingProvider();
if (foldingProvider != null) {
foldingProvider.install(this);
this.foldingProvider= foldingProvider;
}
}
private void uninstallFoldingProvider() {
final var foldingProvider= this.foldingProvider;
if (foldingProvider != null) {
this.foldingProvider= null;
foldingProvider.uninstall();
}
}
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 @Nullable SourceEditorAddon createMarkOccurrencesProvider() {
return null;
}
private void uninstallMarkOccurrencesProvider() {
final var markOccurrencesProvider= this.markOccurrencesProvider;
if (markOccurrencesProvider != null) {
this.markOccurrencesProvider= null;
markOccurrencesProvider.uninstall();
}
}
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) {
var provider= SourceEditor1.this.markOccurrencesProvider;
if (provider == null) {
provider= createMarkOccurrencesProvider();
SourceEditor1.this.markOccurrencesProvider= provider;
}
if (provider != null && SourceEditor1.this.effectSynchonizerCounter == 0) {
provider.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= nonNullAssert(
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(LtkActions.GOTO_MATCHING_BRACKET_COMMAND_ID,
new GotoMatchingBracketHandler(matcher, this));
}
{ final IHandler2 handler= new SpecificContentAssistHandler(this,
this.configurator.getSourceViewerConfiguration().getContentAssist() );
handlerService.activateHandler(LtkActions.SPECIFIC_CONTENT_ASSIST_COMMAND_ID, handler);
}
{ final IHandler2 handler= createToggleCommentHandler();
if (handler != null) {
handlerService.activateHandler(LtkActions.TOGGLE_COMMENT, handler);
}
}
{ final IHandler2 handler= createCorrectIndentHandler();
if (handler != null) {
handlerService.activateHandler(LtkActions.CORRECT_INDENT_COMMAND_ID, handler);
}
}
if (this.foldingEnablement != null) {
this.foldingActionGroup= createFoldingActionGroup();
}
if (this.modelProvider != null) {
this.selectionHistory= new StructureSelectionHistory(this);
handlerService.activateHandler(LtkActions.SELECT_ENCLOSING_COMMAND_ID,
new StructureSelectHandler.Enclosing(this, this.selectionHistory));
handlerService.activateHandler(LtkActions.SELECT_PREVIOUS_COMMAND_ID,
new StructureSelectHandler.Previous(this, this.selectionHistory));
handlerService.activateHandler(LtkActions.SELECT_NEXT_COMMAND_ID,
new StructureSelectHandler.Next(this, this.selectionHistory));
final StructureSelectionHistoryBackHandler backHandler= new StructureSelectionHistoryBackHandler(this, this.selectionHistory);
handlerService.activateHandler(LtkActions.SELECT_LAST_COMMAND_ID, backHandler);
this.selectionHistory.addUpdateListener(backHandler);
}
//WorkbenchHelp.setHelp(action, IJavaHelpContextIds.TOGGLE_COMMENT_ACTION);
}
protected FoldingActionGroup createFoldingActionGroup() {
return new FoldingActionGroup(this, (ProjectionViewer)getSourceViewer());
}
protected @Nullable IHandler2 createToggleCommentHandler() {
final IHandler2 commentHandler= new ToggleCommentHandler(this);
markAsStateDependentHandler(commentHandler, true);
return commentHandler;
}
protected @Nullable 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) {
handler.update();
}
}
@Override
protected void updateStateDependentActions() {
super.updateStateDependentActions();
for (final IHandler2 handler : this.stateUpdateables) {
handler.setEnabled(this);
}
}
@Override
protected void rulerContextMenuAboutToShow(final IMenuManager menu) {
super.rulerContextMenuAboutToShow(menu);
final var foldingActionGroup= this.foldingActionGroup;
if (foldingActionGroup != null) {
final IMenuManager foldingMenu= new MenuManager(EditingMessages.CodeFolding_label, "projection"); //$NON-NLS-1$
menu.appendToGroup(ITextEditorActionConstants.GROUP_RULERS, foldingMenu);
foldingActionGroup.fillMenu(foldingMenu);
}
}
@Override
public TextEditToolSynchronizer getTextEditToolSynchronizer() {
var effectSynchronizer= this.effectSynchronizer;
if (effectSynchronizer == null) {
effectSynchronizer= new EffectSynchonizer();
this.effectSynchronizer= effectSynchronizer;
}
return effectSynchronizer;
}
@Override
@SuppressWarnings("unchecked")
public <T> @Nullable T getAdapter(final Class<T> adapterType) {
if (adapterType == SourceEditor.class) {
return (T)this;
}
if (adapterType == ISourceViewer.class) {
return (T)getSourceViewer();
}
if (adapterType == IContentType.class) {
return (T)this.contentType;
}
if (adapterType == IContentOutlinePage.class) {
var outlinePage= this.outlinePage;
if (outlinePage == null) {
outlinePage= createOutlinePage();
if (outlinePage != null) {
updateOutlinePageInput(outlinePage);
}
this.outlinePage= outlinePage;
}
return (T)outlinePage;
}
if (adapterType == ITemplatesPage.class) {
var templatesPage= this.templatesPage;
if (templatesPage == null) {
templatesPage= createTemplatesPage();
this.templatesPage= templatesPage;
}
return (T)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 @Nullable SourceEditor1OutlinePage createOutlinePage() {
return null;
}
protected void updateOutlinePageInput(final SourceEditor1OutlinePage page) {
}
void handleOutlinePageClosed() {
final var outlinePage= this.outlinePage;
if (outlinePage != null) {
this.outlinePage= null;
resetHighlightRange();
}
}
protected @Nullable ITemplatesPage createTemplatesPage() {
return null;
}
// inject annotation painter workaround
@Override
protected SourceViewerDecorationSupport getSourceViewerDecorationSupport(final ISourceViewer viewer) {
var decorationSupport= this.fSourceViewerDecorationSupport;
if (decorationSupport == null) {
decorationSupport= new org.eclipse.statet.ltk.ui.sourceediting.SourceViewerDecorationSupport(
viewer, getOverviewRuler(), getAnnotationAccess(), getSharedColors() );
configureSourceViewerDecorationSupport(decorationSupport);
this.fSourceViewerDecorationSupport= decorationSupport;
}
return decorationSupport;
}
@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) {
final var modelPostSelection= this.modelPostSelection;
if (modelPostSelection != null) {
modelPostSelection.setUpdateOnSelection(true);
try {
super.selectAndReveal(selectionStart, selectionLength, revealStart, revealLength);
}
finally {
modelPostSelection.setUpdateOnSelection(false);
}
}
else {
super.selectAndReveal(selectionStart, selectionLength, revealStart, revealLength);
}
}
public void setSelection(final ISelection selection, final SelectionWithElementInfoListener listener) {
final var modelPostSelection= this.modelPostSelection;
if (modelPostSelection != null && listener != null) {
final IgnoreActivation activation= 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 var sourceElement= (SourceStructElement<?, ?>)first;
region= LTKSelectionUtils.getRegionToSelect(sourceElement);
final var sourceUnit= sourceElement.getSourceUnit();
final var 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);
{ final var modelPostSelection= this.modelPostSelection;
if (modelPostSelection != null) {
this.modelPostSelection= null;
modelPostSelection.dispose();
}
}
{ final var foldingEnablement= this.foldingEnablement;
if (foldingEnablement != null) {
PreferenceUtils.getInstancePrefs().removePreferenceNodeListener(
foldingEnablement.getQualifier(), this );
uninstallFoldingProvider();
}
}
{ final var markOccurrencesEnablement= this.markOccurrencesEnablement;
if (markOccurrencesEnablement != null) {
PreferenceUtils.getInstancePrefs().removePreferenceNodeListener(
markOccurrencesEnablement.getQualifier(), this );
uninstallMarkOccurrencesProvider();
}
}
super.dispose();
if (this.imageDescriptor != null) {
JFaceResources.getResources().destroyImage(this.imageDescriptor);
this.imageDescriptor= null;
}
this.sourceUnit= null;
this.modelProvider= 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 @NonNull String[] getShowInTargetIds() {
return new @NonNull String[] {
IPageLayout.ID_PROJECT_EXPLORER };
}
}