blob: 57cc340f174b15290ae71ac1359dd218e02d88d3 [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.r.ui.sourceediting;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.IAutoEditStrategy;
import org.eclipse.jface.text.ITextDoubleClickStrategy;
import org.eclipse.jface.text.ITextHover;
import org.eclipse.jface.text.information.IInformationProvider;
import org.eclipse.jface.text.reconciler.IReconciler;
import org.eclipse.jface.text.reconciler.IReconcilingStrategy;
import org.eclipse.jface.text.rules.ITokenScanner;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.texteditor.spelling.SpellingReconcileStrategy;
import org.eclipse.ui.texteditor.spelling.SpellingService;
import org.eclipse.statet.ecommons.preferences.core.util.PreferenceUtils;
import org.eclipse.statet.ecommons.text.ICharPairMatcher;
import org.eclipse.statet.ecommons.text.IIndentSettings;
import org.eclipse.statet.ecommons.text.core.sections.DocContentSections;
import org.eclipse.statet.ecommons.text.ui.presentation.SingleTokenScanner;
import org.eclipse.statet.ecommons.text.ui.settings.TextStyleManager;
import org.eclipse.statet.ecommons.ui.ColorManager;
import org.eclipse.statet.ecommons.ui.ISettingsChangedHandler;
import org.eclipse.statet.base.ext.ui.text.CommentScanner;
import org.eclipse.statet.internal.r.ui.RUIPlugin;
import org.eclipse.statet.internal.r.ui.editors.REditor;
import org.eclipse.statet.internal.r.ui.editors.REditorInformationProvider;
import org.eclipse.statet.internal.r.ui.editors.REditorTextHover;
import org.eclipse.statet.internal.r.ui.editors.RQuickOutlineInformationProvider;
import org.eclipse.statet.ltk.ui.LTKUIPreferences;
import org.eclipse.statet.ltk.ui.sourceediting.EcoReconciler2;
import org.eclipse.statet.ltk.ui.sourceediting.EditorInformationProvider;
import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditorAddon;
import org.eclipse.statet.ltk.ui.sourceediting.SourceEditor1;
import org.eclipse.statet.ltk.ui.sourceediting.SourceEditorViewer;
import org.eclipse.statet.ltk.ui.sourceediting.SourceEditorViewerConfiguration;
import org.eclipse.statet.ltk.ui.sourceediting.SourceUnitReconcilingStrategy;
import org.eclipse.statet.ltk.ui.sourceediting.assist.ContentAssist;
import org.eclipse.statet.ltk.ui.sourceediting.assist.ContentAssistComputerRegistry;
import org.eclipse.statet.ltk.ui.sourceediting.assist.ContentAssistProcessor;
import org.eclipse.statet.ltk.ui.sourceediting.assist.InfoHoverDescriptor;
import org.eclipse.statet.ltk.ui.sourceediting.assist.InfoHoverRegistry;
import org.eclipse.statet.ltk.ui.sourceediting.assist.QuickAssistProcessor;
import org.eclipse.statet.nico.ui.console.ConsolePageEditor;
import org.eclipse.statet.r.core.RCore;
import org.eclipse.statet.r.core.RCoreAccess;
import org.eclipse.statet.r.core.source.RDocumentConstants;
import org.eclipse.statet.r.core.source.RDocumentContentInfo;
import org.eclipse.statet.r.core.source.RHeuristicTokenScanner;
import org.eclipse.statet.r.ui.editors.IRSourceEditor;
import org.eclipse.statet.r.ui.editors.REditorOptions;
import org.eclipse.statet.r.ui.text.r.IRTextTokens;
import org.eclipse.statet.r.ui.text.r.RBracketPairMatcher;
import org.eclipse.statet.r.ui.text.r.RDefaultTextStyleScanner;
import org.eclipse.statet.r.ui.text.r.RDoubleClickStrategy;
import org.eclipse.statet.r.ui.text.r.RInfixOperatorScanner;
import org.eclipse.statet.r.ui.text.r.RoxygenScanner;
/**
* Default Configuration for SourceViewer of R code.
*/
public class RSourceViewerConfiguration extends SourceEditorViewerConfiguration {
private static final String[] CONTENT_TYPES= RDocumentConstants.R_CONTENT_TYPES.toArray(
new String[RDocumentConstants.R_CONTENT_TYPES.size()] );
private RDoubleClickStrategy fDoubleClickStrategy;
private RAutoEditStrategy fAutoEditStrategy;
private RCoreAccess fRCoreAccess;
public RSourceViewerConfiguration(final int flags,
final IPreferenceStore store, final ColorManager colorManager) {
this(RDocumentContentInfo.INSTANCE, flags, null, null, store, null);
}
public RSourceViewerConfiguration(final DocContentSections documentContentInfo, final int flags,
final IRSourceEditor sourceEditor,
final RCoreAccess access,
final IPreferenceStore preferenceStore, final TextStyleManager textStyles) {
super(documentContentInfo, flags, sourceEditor);
setCoreAccess(access);
setup((preferenceStore != null) ? preferenceStore : RUIPlugin.getInstance().getEditorPreferenceStore(),
LTKUIPreferences.getEditorDecorationPreferences(),
LTKUIPreferences.getAssistPreferences() );
setTextStyles(textStyles);
}
protected void setCoreAccess(final RCoreAccess access) {
this.fRCoreAccess = (access != null) ? access : RCore.getWorkbenchAccess();
}
@Override
protected IRSourceEditor getSourceEditor() {
return (IRSourceEditor) super.getSourceEditor();
}
@Override
protected void initTextStyles() {
setTextStyles(RUIPlugin.getInstance().getRTextStyles());
}
@Override
protected void initScanners() {
final TextStyleManager textStyles= getTextStyles();
addScanner(RDocumentConstants.R_DEFAULT_CONTENT_TYPE,
new RDefaultTextStyleScanner(textStyles) );
addScanner(RDocumentConstants.R_INFIX_OPERATOR_CONTENT_TYPE,
new RInfixOperatorScanner(textStyles) );
addScanner(RDocumentConstants.R_STRING_CONTENT_TYPE,
new SingleTokenScanner(textStyles, IRTextTokens.STRING_KEY) );
addScanner(RDocumentConstants.R_COMMENT_CONTENT_TYPE,
new CommentScanner(textStyles, IRTextTokens.COMMENT_KEY, IRTextTokens.TASK_TAG_KEY,
this.fRCoreAccess.getPrefs()) );
addScanner(RDocumentConstants.R_ROXYGEN_CONTENT_TYPE,
new RoxygenScanner(textStyles, this.fRCoreAccess.getPrefs()) );
}
@Override
protected ITokenScanner getScanner(String contentType) {
if (contentType == RDocumentConstants.R_QUOTED_SYMBOL_CONTENT_TYPE) {
contentType= RDocumentConstants.R_STRING_CONTENT_TYPE;
}
return super.getScanner(contentType);
}
public RCoreAccess getRCoreAccess() {
return this.fRCoreAccess;
}
@Override
public List<ISourceEditorAddon> getAddOns() {
final List<ISourceEditorAddon> addons = super.getAddOns();
if (this.fAutoEditStrategy != null) {
addons.add(this.fAutoEditStrategy);
}
return addons;
}
@Override
public void handleSettingsChanged(final Set<String> groupIds, final Map<String, Object> options) {
options.put(ISettingsChangedHandler.PREFERENCEACCESS_KEY, this.fRCoreAccess.getPrefs());
super.handleSettingsChanged(groupIds, options);
}
@Override
public String[] getConfiguredContentTypes(final ISourceViewer sourceViewer) {
return CONTENT_TYPES;
}
@Override
public ICharPairMatcher createPairMatcher() {
return new RBracketPairMatcher(
RHeuristicTokenScanner.create(getDocumentContentInfo()) );
}
@Override
public ITextDoubleClickStrategy getDoubleClickStrategy(final ISourceViewer sourceViewer, final String contentType) {
if (this.fDoubleClickStrategy == null) {
this.fDoubleClickStrategy= new RDoubleClickStrategy(
RHeuristicTokenScanner.create(getDocumentContentInfo()) );
}
return this.fDoubleClickStrategy;
}
@Override
public String[] getDefaultPrefixes(final ISourceViewer sourceViewer, final String contentType) {
return new String[] { "#", "" }; //$NON-NLS-1$ //$NON-NLS-2$
}
@Override
protected IIndentSettings getIndentSettings() {
return this.fRCoreAccess.getRCodeStyle();
}
@Override
public boolean isSmartInsertSupported() {
return true;
}
@Override
public boolean isSmartInsertByDefault() {
return PreferenceUtils.getInstancePrefs().getPreferenceValue(
REditorOptions.SMARTINSERT_BYDEFAULT_ENABLED_PREF );
}
@Override
public IAutoEditStrategy[] getAutoEditStrategies(final ISourceViewer sourceViewer, final String contentType) {
if (getSourceEditor() == null) {
return super.getAutoEditStrategies(sourceViewer, contentType);
}
if (this.fAutoEditStrategy == null) {
this.fAutoEditStrategy = createRAutoEditStrategy();
}
return new IAutoEditStrategy[] { this.fAutoEditStrategy };
}
protected RAutoEditStrategy createRAutoEditStrategy() {
return new RAutoEditStrategy(this.fRCoreAccess, getSourceEditor());
}
@Override
public IReconciler getReconciler(final ISourceViewer sourceViewer) {
final IRSourceEditor editor= getSourceEditor();
if (!(editor instanceof SourceEditor1 || editor instanceof ConsolePageEditor)) {
return null;
}
final EcoReconciler2 reconciler = new EcoReconciler2(editor);
reconciler.setDelay(500);
reconciler.addReconcilingStrategy(new SourceUnitReconcilingStrategy());
if (editor instanceof REditor) {
final IReconcilingStrategy spellingStrategy = getSpellingStrategy(sourceViewer);
if (spellingStrategy != null) {
reconciler.addReconcilingStrategy(spellingStrategy);
}
}
return reconciler;
}
protected IReconcilingStrategy getSpellingStrategy(final ISourceViewer sourceViewer) {
if (!(this.fRCoreAccess.getPrefs().getPreferenceValue(REditorOptions.PREF_SPELLCHECKING_ENABLED)
&& this.fPreferenceStore.getBoolean(SpellingService.PREFERENCE_SPELLING_ENABLED)) ) {
return null;
}
final SpellingService spellingService = EditorsUI.getSpellingService();
if (spellingService.getActiveSpellingEngineDescriptor(this.fPreferenceStore) == null) {
return null;
}
return new SpellingReconcileStrategy(sourceViewer, spellingService);
}
@Override
public void initContentAssist(final ContentAssist assistant) {
final ContentAssistComputerRegistry registry = RUIPlugin.getInstance().getREditorContentAssistRegistry();
final IRSourceEditor editor= getSourceEditor();
final ContentAssistProcessor codeProcessor = new RContentAssistProcessor(assistant,
RDocumentConstants.R_DEFAULT_CONTENT_TYPE, registry, editor);
codeProcessor.setCompletionProposalAutoActivationCharacters(new char[] { '$', '@' });
codeProcessor.setContextInformationAutoActivationCharacters(new char[] { ',' });
assistant.setContentAssistProcessor(codeProcessor, RDocumentConstants.R_DEFAULT_CONTENT_TYPE);
final ContentAssistProcessor symbolProcessor = new RContentAssistProcessor(assistant,
RDocumentConstants.R_QUOTED_SYMBOL_CONTENT_TYPE, registry, editor);
assistant.setContentAssistProcessor(symbolProcessor, RDocumentConstants.R_QUOTED_SYMBOL_CONTENT_TYPE);
final ContentAssistProcessor stringProcessor = new RContentAssistProcessor(assistant,
RDocumentConstants.R_STRING_CONTENT_TYPE, registry, editor);
assistant.setContentAssistProcessor(stringProcessor, RDocumentConstants.R_STRING_CONTENT_TYPE);
final ContentAssistProcessor commentProcessor = new RContentAssistProcessor(assistant,
RDocumentConstants.R_COMMENT_CONTENT_TYPE, registry, editor);
assistant.setContentAssistProcessor(commentProcessor, RDocumentConstants.R_COMMENT_CONTENT_TYPE);
final ContentAssistProcessor roxygenProcessor = new RContentAssistProcessor(assistant,
RDocumentConstants.R_ROXYGEN_CONTENT_TYPE, registry, editor);
roxygenProcessor.setCompletionProposalAutoActivationCharacters(new char[] { '@', '\\' });
assistant.setContentAssistProcessor(roxygenProcessor, RDocumentConstants.R_ROXYGEN_CONTENT_TYPE);
}
@Override
protected QuickAssistProcessor createQuickAssistProcessor() {
final IRSourceEditor editor= getSourceEditor();
if (editor != null) {
return new RQuickAssistProcessor(editor);
}
return null;
}
@Override
protected boolean isInfoHoverDefaultContentType(final String contentType) {
return RDocumentConstants.R_CODE_CONTENT_CONSTRAINT.matches(contentType);
}
@Override
protected InfoHoverRegistry getInfoHoverRegistry() {
return RUIPlugin.getInstance().getREditorInfoHoverRegistry();
}
@Override
protected ITextHover createInfoHover(final InfoHoverDescriptor descriptor) {
final IRSourceEditor editor= getSourceEditor();
if (editor != null) {
return new REditorTextHover(editor, descriptor, this);
}
return null;
}
@Override
protected EditorInformationProvider getInformationProvider() {
final IRSourceEditor editor= getSourceEditor();
if (editor != null) {
return new REditorInformationProvider(editor);
}
return null;
}
@Override
protected void collectHyperlinkDetectorTargets(final Map<String, IAdaptable> targets,
final ISourceViewer sourceViewer) {
targets.put("org.eclipse.statet.r.editorHyperlinks.REditorTarget", getSourceEditor()); //$NON-NLS-1$
}
@Override
protected IInformationProvider getQuickInformationProvider(final ISourceViewer sourceViewer,
final int operation) {
final IRSourceEditor editor= getSourceEditor();
if (editor == null) {
return null;
}
switch (operation) {
case SourceEditorViewer.SHOW_SOURCE_OUTLINE:
return new RQuickOutlineInformationProvider(editor, operation);
default:
return null;
}
}
}