| /*=============================================================================# |
| # Copyright (c) 2007, 2019 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.redocs.tex.r.ui.sourceediting; |
| |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.jface.text.ITextDoubleClickStrategy; |
| import org.eclipse.jface.text.information.IInformationProvider; |
| import org.eclipse.jface.text.presentation.PresentationReconciler; |
| import org.eclipse.jface.text.reconciler.IReconciler; |
| import org.eclipse.jface.text.reconciler.IReconcilingStrategy; |
| import org.eclipse.jface.text.rules.DefaultDamagerRepairer; |
| 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.jcommons.collections.ImCollections; |
| import org.eclipse.statet.jcommons.text.core.TextRegion; |
| |
| import org.eclipse.statet.ecommons.text.ICharPairMatcher; |
| import org.eclipse.statet.ecommons.text.core.sections.DocContentSections; |
| import org.eclipse.statet.ecommons.text.core.treepartitioner.TreePartition; |
| import org.eclipse.statet.ecommons.text.core.treepartitioner.TreePartitionUtils; |
| import org.eclipse.statet.ecommons.text.ui.presentation.SingleTokenScanner; |
| import org.eclipse.statet.ecommons.text.ui.settings.TextStyleManager; |
| |
| import org.eclipse.statet.docmlet.tex.core.TexCoreAccess; |
| import org.eclipse.statet.docmlet.tex.core.source.TexDocumentConstants; |
| import org.eclipse.statet.docmlet.tex.ui.sourceediting.LtxSourceViewerConfiguration; |
| import org.eclipse.statet.internal.redocs.tex.r.RedocsTexRPlugin; |
| import org.eclipse.statet.internal.redocs.tex.r.ui.sourceediting.DocRQuickOutlineInformationProvider; |
| import org.eclipse.statet.internal.redocs.tex.r.ui.sourceediting.RChunkTemplateCompletionComputer; |
| import org.eclipse.statet.ltk.ui.LTKUIPreferences; |
| import org.eclipse.statet.ltk.ui.sourceediting.EcoReconciler2; |
| import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditor; |
| import org.eclipse.statet.ltk.ui.sourceediting.MultiContentSectionSourceViewerConfiguration; |
| import org.eclipse.statet.ltk.ui.sourceediting.SourceEditor1; |
| import org.eclipse.statet.ltk.ui.sourceediting.SourceEditorViewer; |
| import org.eclipse.statet.ltk.ui.sourceediting.assist.ContentAssist; |
| import org.eclipse.statet.ltk.ui.sourceediting.assist.ContentAssistCategory; |
| import org.eclipse.statet.ltk.ui.sourceediting.assist.ContentAssistComputer; |
| import org.eclipse.statet.ltk.ui.sourceediting.assist.ContentAssistProcessor; |
| import org.eclipse.statet.r.core.IRCoreAccess; |
| import org.eclipse.statet.r.core.source.IRDocumentConstants; |
| import org.eclipse.statet.r.core.source.RHeuristicTokenScanner; |
| import org.eclipse.statet.r.core.source.RPartitionNodeType; |
| import org.eclipse.statet.r.ui.editors.IRSourceEditor; |
| import org.eclipse.statet.r.ui.sourceediting.RAutoEditStrategy; |
| import org.eclipse.statet.r.ui.sourceediting.RSourceViewerConfiguration; |
| import org.eclipse.statet.r.ui.text.r.IRTextTokens; |
| import org.eclipse.statet.r.ui.text.r.RDoubleClickStrategy; |
| import org.eclipse.statet.redocs.tex.r.core.source.LtxRweaveBracketPairMatcher; |
| import org.eclipse.statet.redocs.tex.r.core.source.LtxRweaveDocumentContentInfo; |
| import org.eclipse.statet.redocs.tex.r.core.source.TexRweaveDocumentConstants; |
| import org.eclipse.statet.redocs.tex.r.ui.editors.LtxRweaveEditor; |
| |
| |
| /** |
| * Default Configuration for SourceViewer of Sweave (LaTeX+R) code. |
| */ |
| public class LtxRweaveSourceViewerConfiguration extends MultiContentSectionSourceViewerConfiguration { |
| |
| |
| private static final String[] CONTENT_TYPES= TexRweaveDocumentConstants.LTX_R_CONTENT_TYPES.toArray( |
| new String[TexRweaveDocumentConstants.LTX_R_CONTENT_TYPES.size()]); |
| |
| |
| private static class LtxConfiguration extends LtxSourceViewerConfiguration { |
| |
| public LtxConfiguration(final DocContentSections documentContentInfo, final int flags, |
| final ISourceEditor editor, |
| final TexCoreAccess texCoreAccess, |
| final IPreferenceStore preferenceStore) { |
| super(documentContentInfo, flags, editor, texCoreAccess, preferenceStore, null); |
| } |
| |
| @Override |
| protected void setCoreAccess(final TexCoreAccess access) { |
| super.setCoreAccess(access); |
| } |
| |
| } |
| |
| |
| private static class RChunkAutoEditStrategy extends RAutoEditStrategy { |
| |
| public RChunkAutoEditStrategy(final IRCoreAccess coreAccess, final ISourceEditor sourceEditor) { |
| super(coreAccess, sourceEditor); |
| } |
| |
| @Override |
| protected TextRegion getValidRange(final int offset, final TreePartition partition, final int c) { |
| switch (LtxRweaveDocumentContentInfo.INSTANCE.getTypeByPartition(partition.getType())) { |
| case LtxRweaveDocumentContentInfo.R: |
| return TreePartitionUtils.searchNodeUp(partition.getTreeNode(), |
| RPartitionNodeType.DEFAULT_ROOT ); |
| case LtxRweaveDocumentContentInfo.R_CHUNK_CONTROL: |
| switch (c) { |
| case '(': |
| case '[': |
| case '{': |
| case '%': |
| case '\"': |
| case '\'': |
| return TreePartitionUtils.searchPartitionRegion(partition, |
| TexRweaveDocumentConstants.RCHUNK_PARTITION_CONSTRAINT ); |
| } |
| return null; |
| default: |
| return null; |
| } |
| } |
| |
| } |
| |
| private static class RChunkConfiguration extends RSourceViewerConfiguration { |
| |
| public RChunkConfiguration(final DocContentSections documentContentInfo, final int flags, |
| final IRSourceEditor sourceEditor, |
| final IRCoreAccess coreAccess, |
| final IPreferenceStore preferenceStore) { |
| super(documentContentInfo, flags, sourceEditor, coreAccess, preferenceStore, null); |
| } |
| |
| @Override |
| protected void setCoreAccess(final IRCoreAccess access) { |
| super.setCoreAccess(access); |
| } |
| |
| @Override |
| protected TextStyleManager getTextStyles() { |
| return super.getTextStyles(); |
| } |
| |
| @Override |
| protected ITokenScanner getScanner(final String contentType) { |
| return super.getScanner(contentType); |
| } |
| |
| @Override |
| protected RAutoEditStrategy createRAutoEditStrategy() { |
| return new RChunkAutoEditStrategy(getRCoreAccess(), getSourceEditor()); |
| } |
| |
| } |
| |
| |
| private final LtxConfiguration docConfig; |
| private final RChunkConfiguration rConfig; |
| |
| private ITextDoubleClickStrategy rDoubleClickStrategy; |
| |
| |
| public LtxRweaveSourceViewerConfiguration(final int flags) { |
| this(flags, null, null, null, null); |
| } |
| |
| public LtxRweaveSourceViewerConfiguration(final int flags, final LtxRweaveEditor sourceEditor, |
| final TexCoreAccess texCoreAccess, final IRCoreAccess rCoreAccess, |
| final IPreferenceStore preferenceStore) { |
| super(LtxRweaveDocumentContentInfo.INSTANCE, flags, sourceEditor); |
| |
| this.docConfig= new LtxConfiguration(LtxRweaveDocumentContentInfo.INSTANCE, flags, |
| sourceEditor, |
| texCoreAccess, preferenceStore ); |
| this.rConfig= new RChunkConfiguration(LtxRweaveDocumentContentInfo.INSTANCE, (flags & 0xFF), |
| sourceEditor, |
| rCoreAccess, preferenceStore ); |
| |
| registerConfig(LtxRweaveDocumentContentInfo.LTX, this.docConfig); |
| registerConfig(LtxRweaveDocumentContentInfo.R, this.rConfig); |
| |
| setup((preferenceStore != null) ? preferenceStore : RedocsTexRPlugin.getInstance().getEditorPreferenceStore(), |
| LTKUIPreferences.getEditorDecorationPreferences(), |
| LTKUIPreferences.getAssistPreferences() ); |
| } |
| |
| |
| @Override |
| protected void initScanners() { |
| final TextStyleManager textStyles= this.rConfig.getTextStyles(); |
| |
| addScanner(TexRweaveDocumentConstants.RCHUNK_BASE_CONTENT_TYPE, |
| new SingleTokenScanner(textStyles, IRTextTokens.UNDEFINED_KEY )); |
| } |
| |
| @Override |
| protected ITokenScanner getScanner(final String contentType) { |
| if (contentType == TexRweaveDocumentConstants.RCHUNK_CONTROL_CONTENT_TYPE) { |
| return this.rConfig.getScanner(IRDocumentConstants.R_DEFAULT_CONTENT_TYPE); |
| } |
| if (contentType == TexRweaveDocumentConstants.RCHUNK_COMMENT_CONTENT_TYPE) { |
| return this.rConfig.getScanner(IRDocumentConstants.R_COMMENT_CONTENT_TYPE); |
| } |
| return super.getScanner(contentType); |
| } |
| |
| protected void setCoreAccess(final TexCoreAccess texCoreAccess, final IRCoreAccess rCoreAccess) { |
| this.docConfig.setCoreAccess(texCoreAccess); |
| this.rConfig.setCoreAccess(rCoreAccess); |
| } |
| |
| |
| @Override |
| public String[] getConfiguredContentTypes(final ISourceViewer sourceViewer) { |
| return CONTENT_TYPES; |
| } |
| |
| @Override |
| protected void initPresentationReconciler(final PresentationReconciler reconciler) { |
| super.initPresentationReconciler(reconciler); |
| |
| for (final String contentType : TexRweaveDocumentConstants.RCHUNK_CONTENT_TYPES) { |
| final ITokenScanner scanner= getScanner(contentType); |
| if (scanner != null) { |
| final DefaultDamagerRepairer dr= new DefaultDamagerRepairer(scanner); |
| reconciler.setDamager(dr, contentType); |
| reconciler.setRepairer(dr, contentType); |
| } |
| } |
| } |
| |
| |
| @Override |
| public ICharPairMatcher createPairMatcher() { |
| return new LtxRweaveBracketPairMatcher(); |
| } |
| |
| @Override |
| public ITextDoubleClickStrategy getDoubleClickStrategy(final ISourceViewer sourceViewer, final String contentType) { |
| switch (LtxRweaveDocumentContentInfo.INSTANCE.getTypeByPartition(contentType)) { |
| case LtxRweaveDocumentContentInfo.LTX: |
| return this.docConfig.getDoubleClickStrategy(sourceViewer, contentType); |
| case LtxRweaveDocumentContentInfo.R_CHUNK_CONTROL: |
| case LtxRweaveDocumentContentInfo.R: |
| if (this.rDoubleClickStrategy == null) { |
| final RHeuristicTokenScanner scanner= RHeuristicTokenScanner.create(getDocumentContentInfo()); |
| this.rDoubleClickStrategy= new RDoubleClickStrategy(scanner, |
| LtxRweaveBracketPairMatcher.createRChunkPairMatcher(scanner) ); |
| } |
| return this.rDoubleClickStrategy; |
| default: |
| return null; |
| } |
| } |
| |
| |
| protected IReconcilingStrategy getSpellingStrategy(final ISourceViewer sourceViewer) { |
| if (!(this.rConfig.getRCoreAccess().getPrefs().getPreferenceValue(TexRweaveEditingOptions.LTX_SPELLCHECK_ENABLED_PREF) |
| && 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 |
| protected void initContentAssist(final ContentAssist assistant) { |
| super.initContentAssist(assistant); |
| |
| final ContentAssistComputer chunkComputer= new RChunkTemplateCompletionComputer(); |
| |
| final ContentAssistProcessor texProcessor= (ContentAssistProcessor) assistant.getContentAssistProcessor( |
| TexDocumentConstants.LTX_DEFAULT_CONTENT_TYPE ); |
| texProcessor.addCategory(new ContentAssistCategory( |
| TexDocumentConstants.LTX_DEFAULT_CONTENT_TYPE, |
| ImCollections.newList(chunkComputer) )); |
| texProcessor.setCompletionProposalAutoActivationCharacters(new char[] { '\\', '<' }); |
| |
| final ContentAssistProcessor mathProcessor= (ContentAssistProcessor) assistant.getContentAssistProcessor( |
| TexDocumentConstants.LTX_MATH_CONTENT_TYPE ); |
| mathProcessor.addCategory(new ContentAssistCategory( |
| TexDocumentConstants.LTX_MATH_CONTENT_TYPE, |
| ImCollections.newList(chunkComputer) )); |
| mathProcessor.setCompletionProposalAutoActivationCharacters(new char[] { '\\', '<' }); |
| |
| final ContentAssistProcessor controlProcessor= new ContentAssistProcessor(assistant, |
| TexRweaveDocumentConstants.RCHUNK_BASE_CONTENT_TYPE, |
| RedocsTexRPlugin.getInstance().getLtxRweaveEditorContentAssistRegistry(), |
| getSourceEditor() ); |
| controlProcessor.addCategory(new ContentAssistCategory( |
| TexRweaveDocumentConstants.RCHUNK_BASE_CONTENT_TYPE, |
| ImCollections.newList(chunkComputer) )); |
| assistant.setContentAssistProcessor(controlProcessor, TexRweaveDocumentConstants.RCHUNK_BASE_CONTENT_TYPE); |
| assistant.setContentAssistProcessor(controlProcessor, TexRweaveDocumentConstants.RCHUNK_CONTROL_CONTENT_TYPE); |
| } |
| |
| |
| @Override |
| public IReconciler getReconciler(final ISourceViewer sourceViewer) { |
| final ISourceEditor editor= getSourceEditor(); |
| if (!(editor instanceof SourceEditor1)) { |
| return null; |
| } |
| final EcoReconciler2 reconciler= (EcoReconciler2) this.docConfig.getReconciler(sourceViewer); |
| if (reconciler != null) { |
| final IReconcilingStrategy spellingStrategy= getSpellingStrategy(sourceViewer); |
| if (spellingStrategy != null) { |
| reconciler.addReconcilingStrategy(spellingStrategy); |
| } |
| } |
| return reconciler; |
| } |
| |
| |
| @Override |
| protected IInformationProvider getQuickInformationProvider(final ISourceViewer sourceViewer, |
| final int operation) { |
| final ISourceEditor editor= getSourceEditor(); |
| if (editor == null) { |
| return null; |
| } |
| switch (operation) { |
| case SourceEditorViewer.SHOW_SOURCE_OUTLINE: |
| return new DocRQuickOutlineInformationProvider(editor, operation); |
| default: |
| return null; |
| } |
| } |
| |
| } |