| /*=============================================================================# |
| # 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; |
| } |
| } |
| |
| } |