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