blob: a10bfc68b8e0ed93df868b99fe6e8f610b20f5c1 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2007, 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.redocs.wikitext.r.ui.editors;
import static org.eclipse.statet.ecommons.ui.actions.UIActions.ADDITIONS_GROUP_ID;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
import org.eclipse.core.commands.IHandler2;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.text.AbstractDocument;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.BadPartitioningException;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IPageLayout;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.menus.CommandContributionItem;
import org.eclipse.ui.menus.CommandContributionItemParameter;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
import org.eclipse.ui.texteditor.ITextEditorActionConstants;
import org.eclipse.ui.texteditor.templates.ITemplatesPage;
import org.eclipse.statet.jcommons.collections.ImCollections;
import org.eclipse.statet.jcommons.collections.ImList;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.jcommons.text.core.TextRegion;
import org.eclipse.statet.base.ui.IStatetUIMenuIds;
import org.eclipse.statet.docmlet.base.ui.DocmlBaseUI;
import org.eclipse.statet.docmlet.base.ui.markuphelp.MarkupHelpContextProvider;
import org.eclipse.statet.docmlet.base.ui.processing.actions.RunDocProcessingOnSaveExtension;
import org.eclipse.statet.docmlet.base.ui.sourceediting.DocEditor;
import org.eclipse.statet.docmlet.wikitext.core.WikitextCore;
import org.eclipse.statet.docmlet.wikitext.core.ast.WikitextAstNode;
import org.eclipse.statet.docmlet.wikitext.core.model.WikitextModel;
import org.eclipse.statet.docmlet.wikitext.ui.WikitextUI;
import org.eclipse.statet.docmlet.wikitext.ui.editors.WikidocDefaultFoldingProvider;
import org.eclipse.statet.docmlet.wikitext.ui.editors.WikitextMarkOccurrencesLocator;
import org.eclipse.statet.docmlet.wikitext.ui.sourceediting.WikitextEditingSettings;
import org.eclipse.statet.internal.redocs.wikitext.r.RedocsWikitextRPlugin;
import org.eclipse.statet.internal.redocs.wikitext.r.ui.editors.WikidocRweaveEditorTemplatesPage;
import org.eclipse.statet.internal.redocs.wikitext.r.ui.editors.WikidocRweaveOutlinePage;
import org.eclipse.statet.ltk.ast.core.util.AstSelection;
import org.eclipse.statet.ltk.model.core.elements.ISourceUnitModelInfo;
import org.eclipse.statet.ltk.ui.LTKUI;
import org.eclipse.statet.ltk.ui.sourceediting.AbstractMarkOccurrencesProvider;
import org.eclipse.statet.ltk.ui.sourceediting.ForwardSourceDocumentProvider;
import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditorAddon;
import org.eclipse.statet.ltk.ui.sourceediting.SourceEditor1;
import org.eclipse.statet.ltk.ui.sourceediting.SourceEditor1OutlinePage;
import org.eclipse.statet.ltk.ui.sourceediting.SourceEditorViewer;
import org.eclipse.statet.ltk.ui.sourceediting.SourceEditorViewerConfigurator;
import org.eclipse.statet.ltk.ui.sourceediting.folding.FoldingEditorAddon;
import org.eclipse.statet.r.core.IRCoreAccess;
import org.eclipse.statet.r.core.RCore;
import org.eclipse.statet.r.core.rsource.ast.RAstNode;
import org.eclipse.statet.r.core.source.IRDocumentConstants;
import org.eclipse.statet.r.launching.RCodeLaunching;
import org.eclipse.statet.r.ui.RUI;
import org.eclipse.statet.r.ui.editors.IRSourceEditor;
import org.eclipse.statet.r.ui.editors.RCorrectIndentHandler;
import org.eclipse.statet.r.ui.editors.RDefaultFoldingProvider;
import org.eclipse.statet.r.ui.editors.RMarkOccurrencesLocator;
import org.eclipse.statet.r.ui.sourceediting.InsertAssignmentHandler;
import org.eclipse.statet.redocs.r.core.source.DocContentSectionsRweaveExtension;
import org.eclipse.statet.redocs.r.ui.RedocsRUI;
import org.eclipse.statet.redocs.r.ui.sourceediting.actions.RweaveToggleCommentHandler;
import org.eclipse.statet.redocs.wikitext.r.core.model.WikidocRweaveSourceUnit;
import org.eclipse.statet.redocs.wikitext.r.core.source.RweaveMarkupLanguage;
import org.eclipse.statet.redocs.wikitext.r.core.source.WikidocRweaveDocumentContentInfo;
import org.eclipse.statet.redocs.wikitext.r.core.source.WikidocRweaveDocumentSetupParticipant;
import org.eclipse.statet.redocs.wikitext.r.ui.WikitextRweaveUI;
import org.eclipse.statet.redocs.wikitext.r.ui.sourceediting.WikidocRweaveSourceViewerConfiguration;
import org.eclipse.statet.redocs.wikitext.r.ui.sourceediting.WikidocRweaveSourceViewerConfigurator;
import org.eclipse.statet.rj.renv.core.REnv;
/**
* Editor for Wikitext-R documents.
*/
public abstract class WikidocRweaveDocEditor extends SourceEditor1 implements WikidocRweaveEditor,
DocEditor, MarkupHelpContextProvider {
private static final ImList<String> KEY_CONTEXTS= ImCollections.newIdentityList(
WikitextUI.EDITOR_CONTEXT_ID,
DocmlBaseUI.DOC_EDITOR_CONTEXT_ID,
RedocsRUI.RWEAVE_EDITOR_CONTEXT_ID );
private static final ImList<String> CONTEXT_IDS= ImCollections.concatList(
ACTION_SET_CONTEXT_IDS, KEY_CONTEXTS );
private static class ThisMarkOccurrencesProvider extends AbstractMarkOccurrencesProvider {
private final WikitextMarkOccurrencesLocator docLocator= new WikitextMarkOccurrencesLocator();
private final RMarkOccurrencesLocator rLocator= new RMarkOccurrencesLocator();
public ThisMarkOccurrencesProvider(final SourceEditor1 editor) {
super(editor, IRDocumentConstants.R_DEFAULT_CONTENT_CONSTRAINT);
}
@Override
protected void doUpdate(final RunData run, final ISourceUnitModelInfo info,
final AstSelection astSelection, final ITextSelection orgSelection)
throws BadLocationException, BadPartitioningException, UnsupportedOperationException {
if (astSelection.getCovering() instanceof WikitextAstNode) {
this.docLocator.run(run, info, astSelection, orgSelection);
}
else if (astSelection.getCovering() instanceof RAstNode) {
this.rLocator.run(run, info, astSelection, orgSelection);
}
}
}
private static class ThisCorrectIndentHandler extends RCorrectIndentHandler {
public ThisCorrectIndentHandler(final IRSourceEditor editor) {
super(editor);
}
@Override
protected List<? extends TextRegion> getCodeRanges(final AbstractDocument document,
final ITextSelection selection) throws BadLocationException {
return WikidocRweaveDocumentContentInfo.INSTANCE.getRChunkCodeRegions(document,
selection.getOffset(), selection.getLength() );
}
}
private final WikidocRweaveDocumentSetupParticipant documentSetup;
private WikidocRweaveSourceViewerConfigurator combinedConfig;
private final RunDocProcessingOnSaveExtension autoDocProcessing;
public WikidocRweaveDocEditor(final IContentType contentType,
final WikidocRweaveDocumentSetupParticipant documentSetup) {
super(contentType);
if (documentSetup == null) {
throw new NullPointerException("documentSetup"); //$NON-NLS-1$
}
this.documentSetup= documentSetup;
this.autoDocProcessing= new RunDocProcessingOnSaveExtension(this);
initializeEditor();
}
@Override
protected void initializeEditor() {
if (this.documentSetup == null) {
return;
}
super.initializeEditor();
setEditorContextMenuId("org.eclipse.statet.redocs.menus.WikidocRweaveEditorContextMenu"); //$NON-NLS-1$
setRulerContextMenuId("org.eclipse.statet.redocs.menus.WikidocRweaveEditorRulerMenu"); //$NON-NLS-1$
}
@Override
protected SourceEditorViewerConfigurator createConfiguration() {
setDocumentProvider(new ForwardSourceDocumentProvider(
RedocsWikitextRPlugin.getInstance().getDocRDocumentProvider(),
this.documentSetup ));
enableStructuralFeatures(WikitextModel.getWikidocModelManager(),
WikitextEditingSettings.FOLDING_ENABLED_PREF,
WikitextEditingSettings.MARKOCCURRENCES_ENABLED_PREF );
this.combinedConfig= new WikidocRweaveSourceViewerConfigurator(
this.documentSetup,
WikitextCore.WORKBENCH_ACCESS, RCore.WORKBENCH_ACCESS,
new WikidocRweaveSourceViewerConfiguration(0, this, null, null, null) );
return this.combinedConfig;
}
@Override
protected int getSourceViewerFlags() {
return SourceEditorViewer.VARIABLE_LINE_HEIGHT;
}
@Override
protected Collection<String> getContextIds() {
return CONTEXT_IDS;
}
@Override
protected ISourceEditorAddon createCodeFoldingProvider() {
return new FoldingEditorAddon(new WikidocDefaultFoldingProvider(Collections.singletonMap(
RweaveMarkupLanguage.EMBEDDED_R, new RDefaultFoldingProvider() )));
}
@Override
protected ISourceEditorAddon createMarkOccurrencesProvider() {
return new ThisMarkOccurrencesProvider(this);
}
@Override
public IRCoreAccess getRCoreAccess() {
return this.combinedConfig.getRCoreAccess();
}
@Override
public DocContentSectionsRweaveExtension getDocumentContentInfo() {
return (DocContentSectionsRweaveExtension) super.getDocumentContentInfo();
}
@Override
public WikidocRweaveSourceUnit getSourceUnit() {
return (WikidocRweaveSourceUnit) super.getSourceUnit();
}
@Override
protected void setupConfiguration(final IEditorInput newInput) {
super.setupConfiguration(newInput);
final WikidocRweaveSourceUnit su= getSourceUnit();
this.combinedConfig.setSource(
(su != null) ? su.getWikitextCoreAccess() : WikitextCore.WORKBENCH_ACCESS,
(su != null) ? su.getRCoreAccess() : RCore.WORKBENCH_ACCESS );
this.autoDocProcessing.setAutoRunEnabled(false);
}
@Override
protected void handlePreferenceStoreChanged(final org.eclipse.jface.util.PropertyChangeEvent event) {
if (AbstractDecoratedTextEditorPreferenceConstants.EDITOR_TAB_WIDTH.equals(event.getProperty())
|| AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SPACES_FOR_TABS.equals(event.getProperty())) {
return;
}
super.handlePreferenceStoreChanged(event);
}
@Override
protected boolean isTabsToSpacesConversionEnabled() {
return false;
}
public void updateSettings(final boolean indentChanged) {
if (indentChanged) {
updateIndentPrefixes();
}
}
@Override
protected void collectContextMenuPreferencePages(final List<String> pageIds) {
super.collectContextMenuPreferencePages(pageIds);
pageIds.add(WikitextRweaveUI.EDITOR_PREF_PAGE_ID);
pageIds.add(WikitextUI.EDITOR_PREF_PAGE_ID);
pageIds.add("org.eclipse.mylyn.internal.wikitext.ui.editor.preferences.WikiTextTemplatePreferencePage"); //$NON-NLS-1$
pageIds.add("org.eclipse.statet.docmlet.preferencePages.WikitextCodeStyle"); //$NON-NLS-1$
pageIds.add("org.eclipse.statet.r.preferencePages.REditorOptions"); //$NON-NLS-1$
pageIds.add("org.eclipse.statet.r.preferencePages.RTextStyles"); //$NON-NLS-1$
pageIds.add("org.eclipse.statet.r.preferencePages.REditorTemplates"); //$NON-NLS-1$
pageIds.add("org.eclipse.statet.r.preferencePages.RCodeStyle"); //$NON-NLS-1$
}
@Override
protected void createActions() {
super.createActions();
final IHandlerService handlerService= getServiceLocator()
.getService(IHandlerService.class);
{ final IHandler2 handler= new InsertAssignmentHandler(this);
handlerService.activateHandler(LTKUI.INSERT_ASSIGNMENT_COMMAND_ID, handler);
markAsStateDependentHandler(handler, true);
}
}
@Override
protected IHandler2 createToggleCommentHandler() {
final IHandler2 handler= new RweaveToggleCommentHandler(this) {
@Override
protected Pattern getPrefixPattern(final String contentType, final String prefix) {
if (prefix.equals("<!--")) { //$NON-NLS-1$
return HTML_SPACE_PREFIX_PATTERN;
}
return super.getPrefixPattern(contentType, prefix);
}
@Override
protected Pattern getPostfixPattern(final String contentType, final String prefix) {
if (prefix.equals("<!--")) { //$NON-NLS-1$
return HTML_SPACE_POSTFIX_PATTERN;
}
return super.getPostfixPattern(contentType, prefix);
}
@Override
protected void doPrefixPrimary(final AbstractDocument document, final IRegion block)
throws BadLocationException, BadPartitioningException {
doPrefix(document, block, "<!-- ", " -->"); //$NON-NLS-1$ //$NON-NLS-2$
}
};
markAsStateDependentHandler(handler, true);
return handler;
}
@Override
protected IHandler2 createCorrectIndentHandler() {
final IHandler2 handler= new ThisCorrectIndentHandler(this);
markAsStateDependentHandler(handler, true);
return handler;
}
@Override
protected void editorContextMenuAboutToShow(final IMenuManager m) {
super.editorContextMenuAboutToShow(m);
final WikidocRweaveSourceUnit su= getSourceUnit();
m.insertBefore(ADDITIONS_GROUP_ID, new Separator(IStatetUIMenuIds.GROUP_SUBMIT_MENU_ID));
final IContributionItem additions= m.find(ADDITIONS_GROUP_ID);
if (additions != null) {
additions.setVisible(false);
}
m.remove(ITextEditorActionConstants.SHIFT_RIGHT);
m.remove(ITextEditorActionConstants.SHIFT_LEFT);
m.appendToGroup(IStatetUIMenuIds.GROUP_SUBMIT_MENU_ID, new CommandContributionItem(
new CommandContributionItemParameter(getSite(), null,
RCodeLaunching.SUBMIT_SELECTION_COMMAND_ID,
CommandContributionItem.STYLE_PUSH )));
m.appendToGroup(IStatetUIMenuIds.GROUP_SUBMIT_MENU_ID, new CommandContributionItem(
new CommandContributionItemParameter(getSite(), null,
RCodeLaunching.SUBMIT_UPTO_SELECTION_COMMAND_ID,
CommandContributionItem.STYLE_PUSH )));
m.appendToGroup(ITextEditorActionConstants.GROUP_SETTINGS, new CommandContributionItem(
new CommandContributionItemParameter(getSite(), null,
DocmlBaseUI.CONFIGURE_MARKUP_COMMAND_ID, null,
null, null, null,
"Configure Markup...", "M", null,
CommandContributionItem.STYLE_PUSH, null, false )));
}
@Override
protected void editorSaved() {
super.editorSaved();
this.autoDocProcessing.onEditorSaved();
}
@Override
protected SourceEditor1OutlinePage createOutlinePage() {
return new WikidocRweaveOutlinePage(this);
}
@Override
protected ITemplatesPage createTemplatesPage() {
return new WikidocRweaveEditorTemplatesPage(this);
}
@Override
public String[] getShowInTargetIds() {
return new String[] { IPageLayout.ID_PROJECT_EXPLORER, IPageLayout.ID_OUTLINE, RUI.R_HELP_VIEW_ID };
}
@Override
public @Nullable String getHelpContentId() {
return WikitextUI.getMarkupHelpContentIdFor(this.documentSetup.getMarkupLanguage());
}
@Override
@SuppressWarnings("unchecked")
public <T> T getAdapter(final Class<T> adapterType) {
if (adapterType == MarkupHelpContextProvider.class) {
return (T) this;
}
if (adapterType == RunDocProcessingOnSaveExtension.class) {
return (T) this.autoDocProcessing;
}
if (adapterType == REnv.class) {
return (T) getRCoreAccess().getREnv();
}
return super.getAdapter(adapterType);
}
}