blob: 71c4a41064c9abf3e292c4f8545549ca1b6b9f20 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2017 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
*******************************************************************************/
package org.eclipse.dltk.ui.text;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.dltk.annotations.Nullable;
import org.eclipse.dltk.compiler.task.ITodoTaskPreferences;
import org.eclipse.dltk.compiler.util.Util;
import org.eclipse.dltk.internal.ui.editor.ModelElementHyperlinkDetector;
import org.eclipse.dltk.internal.ui.editor.ScriptEditor;
import org.eclipse.dltk.internal.ui.editor.ScriptSourceViewer;
import org.eclipse.dltk.internal.ui.text.HTMLAnnotationHover;
import org.eclipse.dltk.internal.ui.text.HTMLTextPresenter;
import org.eclipse.dltk.internal.ui.text.ScriptCompositeReconcilingStrategy;
import org.eclipse.dltk.internal.ui.text.ScriptElementProvider;
import org.eclipse.dltk.internal.ui.text.ScriptReconciler;
import org.eclipse.dltk.internal.ui.text.hover.EditorTextHoverDescriptor;
import org.eclipse.dltk.internal.ui.text.hover.EditorTextHoverProxy;
import org.eclipse.dltk.internal.ui.text.hover.ScriptInformationProvider;
import org.eclipse.dltk.ui.CodeFormatterConstants;
import org.eclipse.dltk.ui.DLTKUIPlugin;
import org.eclipse.dltk.ui.actions.IScriptEditorActionDefinitionIds;
import org.eclipse.dltk.ui.formatter.ScriptFormatterManager;
import org.eclipse.dltk.ui.formatter.ScriptFormattingStrategy;
import org.eclipse.dltk.ui.text.completion.ContentAssistPreference;
import org.eclipse.dltk.ui.text.spelling.SpellCheckDelegate;
import org.eclipse.dltk.ui.text.util.AutoEditUtils;
import org.eclipse.dltk.ui.text.util.TabStyle;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.AbstractInformationControlManager;
import org.eclipse.jface.text.DefaultInformationControl;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.ITextHover;
import org.eclipse.jface.text.ITextViewerExtension2;
import org.eclipse.jface.text.contentassist.ContentAssistant;
import org.eclipse.jface.text.contentassist.IContentAssistant;
import org.eclipse.jface.text.formatter.IContentFormatter;
import org.eclipse.jface.text.formatter.MultiPassContentFormatter;
import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
import org.eclipse.jface.text.information.IInformationPresenter;
import org.eclipse.jface.text.information.IInformationProvider;
import org.eclipse.jface.text.information.InformationPresenter;
import org.eclipse.jface.text.quickassist.IQuickAssistAssistant;
import org.eclipse.jface.text.reconciler.IReconciler;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.IAnnotationHover;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.editors.text.TextSourceViewerConfiguration;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
import org.eclipse.ui.texteditor.HyperlinkDetectorRegistry;
import org.eclipse.ui.texteditor.ITextEditor;
public abstract class ScriptSourceViewerConfiguration
extends TextSourceViewerConfiguration {
private IColorManager fColorManager;
private ITextEditor fTextEditor;
private String fDocumentPartitioning;
public ScriptSourceViewerConfiguration(IColorManager colorManager,
IPreferenceStore preferenceStore, ITextEditor editor,
String partitioning) {
super(preferenceStore);
fColorManager = colorManager;
fTextEditor = editor;
fDocumentPartitioning = partitioning;
initializeScanners();
}
protected void initializeScanners() {
}
/**
* Returns a scanner that is capable of detecting single line comments that
* contain todo tasks.
*
* <p>
* Clients should make a call to this method to create the scanner in their
* overriden <code>initalizeScanners()</code> implementation.
* </p>
*
* @param commentColor
* comment color key
* @param tagColor
* tag color key
*
* @see #createCommentScanner(String, String, ITodoTaskPreferences)
*/
protected final AbstractScriptScanner createCommentScanner(
String commentColor, String tagColor) {
return createCommentScanner(commentColor, tagColor,
new TodoTaskPreferencesOnPreferenceStore(fPreferenceStore));
}
/**
* Returns a scanner that is capable of detecting single line comments that
* contain todo tasks.
*
* <p>
* Default implementation returns an instance of
* {@link ScriptCommentScanner}. Clients that need to define an alternate
* comment scanner implementation should override this method.
* </p>
*
* @see #createCommentScanner(String, String)
*/
protected AbstractScriptScanner createCommentScanner(String commentColor,
String tagColor, ITodoTaskPreferences taskPrefs) {
return new ScriptCommentScanner(getColorManager(), fPreferenceStore,
commentColor, tagColor, taskPrefs);
}
@Override
public String getConfiguredDocumentPartitioning(
ISourceViewer sourceViewer) {
if (fDocumentPartitioning != null)
return fDocumentPartitioning;
return super.getConfiguredDocumentPartitioning(sourceViewer);
}
protected IColorManager getColorManager() {
return this.fColorManager;
}
/**
* @since 3.0
*/
public IPreferenceStore getPreferenceStore() {
return fPreferenceStore;
}
public ITextEditor getEditor() {
return this.fTextEditor;
}
@Override
public IReconciler getReconciler(ISourceViewer sourceViewer) {
final ITextEditor editor = getEditor();
if (editor != null && editor.isEditable()) {
ScriptCompositeReconcilingStrategy strategy = new ScriptCompositeReconcilingStrategy(
editor, getConfiguredDocumentPartitioning(sourceViewer),
createSpellCheckDelegate());
ScriptReconciler reconciler = new ScriptReconciler(editor, strategy,
false);
reconciler.setIsAllowedToModifyDocument(false);
reconciler.setIsIncrementalReconciler(false);
reconciler.setProgressMonitor(new NullProgressMonitor());
reconciler.setDelay(500);
return reconciler;
}
return null;
}
public boolean affectsTextPresentation(PropertyChangeEvent event) {
return false;
}
public void handlePropertyChangeEvent(PropertyChangeEvent event) {
}
/*
* @see SourceViewerConfiguration#getDefaultPrefixes(ISourceViewer,String)
*/
@Override
public String[] getDefaultPrefixes(ISourceViewer sourceViewer,
String contentType) {
return new String[] { getCommentPrefix(), "" }; //$NON-NLS-1$
}
/**
* Returns the comment prefix.
*
* <p>
* Default implementation returns a <code>#</code>, sub-classes may override
* if their language uses a different prefix.
* </p>
*/
protected String getCommentPrefix() {
return "#"; //$NON-NLS-1$
}
/**
* Returns the outline presenter control creator. The creator is a factory
* creating outline presenter controls for the given source viewer. This
* implementation always returns a creator for
* <code>ScriptOutlineInformationControl</code> instances.
*
* @param sourceViewer
* the source viewer to be configured by this configuration
* @param commandId
* the ID of the command that opens this control
* @return an information control creator
*
*/
protected IInformationControlCreator getOutlinePresenterControlCreator(
ISourceViewer sourceViewer, final String commandId) {
return parent -> {
int shellStyle = SWT.RESIZE;
int treeStyle = SWT.V_SCROLL | SWT.H_SCROLL;
return new ScriptOutlineInformationControl(parent, shellStyle,
treeStyle, commandId, fPreferenceStore);
};
}
public IInformationPresenter getOutlinePresenter(ISourceViewer sourceViewer,
boolean doCodeResolve) {
InformationPresenter presenter;
if (doCodeResolve)
presenter = new InformationPresenter(
getOutlinePresenterControlCreator(sourceViewer,
IScriptEditorActionDefinitionIds.OPEN_STRUCTURE));
else
presenter = new InformationPresenter(
getOutlinePresenterControlCreator(sourceViewer,
IScriptEditorActionDefinitionIds.SHOW_OUTLINE));
presenter.setDocumentPartitioning(
getConfiguredDocumentPartitioning(sourceViewer));
presenter.setAnchor(AbstractInformationControlManager.ANCHOR_GLOBAL);
IInformationProvider provider = new ScriptElementProvider(getEditor(),
doCodeResolve);
presenter.setInformationProvider(provider,
IDocument.DEFAULT_CONTENT_TYPE);
initializeQuickOutlineContexts(presenter, provider);
for (String contentType : getOutlinePresenterContentTypes(sourceViewer,
doCodeResolve)) {
if (IDocument.DEFAULT_CONTENT_TYPE.equals(contentType))
continue;
if (presenter.getInformationProvider(contentType) != null)
continue;
presenter.setInformationProvider(provider, contentType);
}
presenter.setSizeConstraints(50, 20, true, false);
return presenter;
}
protected String[] getOutlinePresenterContentTypes(
ISourceViewer sourceViewer, boolean doCodeResolve) {
return getConfiguredContentTypes(sourceViewer);
}
@Deprecated
protected void initializeQuickOutlineContexts(
InformationPresenter presenter, IInformationProvider provider) {
}
public IInformationPresenter getHierarchyPresenter(
ScriptSourceViewer viewer, boolean b) {
return null;
}
protected IDialogSettings getSettings(String sectionName) {
IDialogSettings settings = DLTKUIPlugin.getDefault().getDialogSettings()
.getSection(sectionName);
if (settings == null)
settings = DLTKUIPlugin.getDefault().getDialogSettings()
.addNewSection(sectionName);
return settings;
}
@Override
public IHyperlinkDetector[] getHyperlinkDetectors(
ISourceViewer sourceViewer) {
if (!fPreferenceStore.getBoolean(
AbstractDecoratedTextEditorPreferenceConstants.EDITOR_HYPERLINKS_ENABLED))
return null;
final IHyperlinkDetector[] inheritedDetectors = super.getHyperlinkDetectors(
sourceViewer);
if (fTextEditor == null) {
return inheritedDetectors;
}
int resultLength = 1;
if (inheritedDetectors != null) {
resultLength += inheritedDetectors.length;
}
final IHyperlinkDetector[] additionalDetectors = getAdditionalRegisteredHyperlinkDetectors(
sourceViewer);
if (additionalDetectors != null) {
resultLength += additionalDetectors.length;
}
final IHyperlinkDetector[] detectors = new IHyperlinkDetector[resultLength];
int resultIndex = 0;
if (inheritedDetectors != null) {
System.arraycopy(inheritedDetectors, 0, detectors, resultIndex,
inheritedDetectors.length);
resultIndex += inheritedDetectors.length;
}
detectors[resultIndex++] = new ModelElementHyperlinkDetector(
fTextEditor);
if (additionalDetectors != null) {
System.arraycopy(additionalDetectors, 0, detectors, resultIndex,
additionalDetectors.length);
resultIndex += additionalDetectors.length;
}
return detectors;
}
/**
* Returns the additional registered hyperlink detectors which are used to
* detect hyperlinks in the given source viewer.
*
* @param sourceViewer
* the source viewer to be configured by this configuration
* @return an array with hyperlink detectors or <code>null</code> if no
* hyperlink detectors are registered
* @since 5.0
*/
@Nullable
private final IHyperlinkDetector[] getAdditionalRegisteredHyperlinkDetectors(
ISourceViewer sourceViewer) {
final Map<String, IAdaptable> targets = getAdditionalHyperlinkDetectorTargets(
sourceViewer);
Assert.isNotNull(targets);
if (targets.isEmpty()) {
return null;
}
final HyperlinkDetectorRegistry registry = EditorsUI
.getHyperlinkDetectorRegistry();
List<IHyperlinkDetector> result = null;
for (Map.Entry<String, IAdaptable> target : targets.entrySet()) {
final IHyperlinkDetector[] detectors = registry
.createHyperlinkDetectors(target.getKey(),
target.getValue());
if (detectors != null && detectors.length != 0) {
if (result == null) {
result = new ArrayList<>();
}
Collections.addAll(result, detectors);
}
}
return result != null
? result.toArray(new IHyperlinkDetector[result.size()])
: null;
}
/**
* Similar to {@link #getHyperlinkDetectorTargets(ISourceViewer)}, but these
* detectors are always added in the end.
*
* @since 5.0
*/
protected Map<String, IAdaptable> getAdditionalHyperlinkDetectorTargets(
ISourceViewer sourceViewer) {
return new HashMap<>();
}
/*
* @see
* SourceViewerConfiguration#getConfiguredTextHoverStateMasks(ISourceViewer,
* String)
*/
@Override
public int[] getConfiguredTextHoverStateMasks(ISourceViewer sourceViewer,
String contentType) {
final String natureId = getNatureId();
if (natureId == null) {
return null;
}
EditorTextHoverDescriptor[] hoverDescs = DLTKUIPlugin.getDefault()
.getEditorTextHoverDescriptors(fPreferenceStore, natureId);
int stateMasks[] = new int[hoverDescs.length];
int stateMasksLength = 0;
for (int i = 0; i < hoverDescs.length; i++) {
if (hoverDescs[i].isEnabled()) {
int j = 0;
int stateMask = hoverDescs[i].getStateMask();
while (j < stateMasksLength) {
if (stateMasks[j] == stateMask)
break;
j++;
}
if (j == stateMasksLength)
stateMasks[stateMasksLength++] = stateMask;
}
}
if (stateMasksLength == hoverDescs.length)
return stateMasks;
int[] shortenedStateMasks = new int[stateMasksLength];
System.arraycopy(stateMasks, 0, shortenedStateMasks, 0,
stateMasksLength);
return shortenedStateMasks;
}
/*
* @see SourceViewerConfiguration#getTextHover(ISourceViewer, String, int)
*/
@Override
public ITextHover getTextHover(ISourceViewer sourceViewer,
String contentType, int stateMask) {
final String natureId = getNatureId();
if (natureId == null) {
return null;
}
EditorTextHoverDescriptor[] hoverDescs = DLTKUIPlugin.getDefault()
.getEditorTextHoverDescriptors(fPreferenceStore, natureId);
int i = 0;
while (i < hoverDescs.length) {
if (hoverDescs[i].isEnabled()
&& hoverDescs[i].getStateMask() == stateMask)
return new EditorTextHoverProxy(hoverDescs[i], getEditor(),
fPreferenceStore);
i++;
}
return null;
}
private String getNatureId() {
final ITextEditor editor = getEditor();
if (editor == null || !(editor instanceof ScriptEditor)) {
return null;
}
return ((ScriptEditor) editor).getLanguageToolkit().getNatureId();
}
@Override
public ITextHover getTextHover(ISourceViewer sourceViewer,
String contentType) {
return getTextHover(sourceViewer, contentType,
ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK);
}
/**
* Returns the information presenter control creator. The creator is a
* factory creating the presenter controls for the given source viewer. This
* implementation always returns a creator for
* <code>DefaultInformationControl</code> instances.
*
* @param sourceViewer
* the source viewer to be configured by this configuration
* @return an information control creator
*
*/
private IInformationControlCreator getInformationPresenterControlCreator(
ISourceViewer sourceViewer) {
return parent -> {
int shellStyle = SWT.RESIZE | SWT.TOOL;
int style = SWT.V_SCROLL | SWT.H_SCROLL;
return new DefaultInformationControl(parent, shellStyle, style,
new HTMLTextPresenter(false));
};
}
@Override
public IInformationPresenter getInformationPresenter(
ISourceViewer sourceViewer) {
InformationPresenter presenter = new InformationPresenter(
getInformationPresenterControlCreator(sourceViewer));
presenter.setDocumentPartitioning(
getConfiguredDocumentPartitioning(sourceViewer));
IInformationProvider provider = new ScriptInformationProvider(
getEditor());
presenter.setInformationProvider(provider,
IDocument.DEFAULT_CONTENT_TYPE);
presenter.setSizeConstraints(60, 10, true, true);
return presenter;
}
@Override
public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) {
if (getEditor() != null) {
ContentAssistant assistant = new ContentAssistant();
assistant.setDocumentPartitioning(
getConfiguredDocumentPartitioning(sourceViewer));
assistant.setRestoreCompletionProposalSize(
getSettings("completion_proposal_size")); //$NON-NLS-1$
assistant.setContextInformationPopupOrientation(
IContentAssistant.CONTEXT_INFO_ABOVE);
assistant.setInformationControlCreator(
getInformationControlCreator(sourceViewer));
alterContentAssistant(assistant);
getContentAssistPreference().configure(assistant, fPreferenceStore);
assistant.enableColoredLabels(true);
return assistant;
}
return null;
}
protected abstract ContentAssistPreference getContentAssistPreference();
protected void alterContentAssistant(ContentAssistant assistant) {
// empty implementation
}
public String getFontPropertyPreferenceKey() {
return JFaceResources.TEXT_FONT;
}
public void changeContentAssistantConfiguration(ContentAssistant c,
PropertyChangeEvent event) {
getContentAssistPreference().changeConfiguration(c, fPreferenceStore,
event);
}
@Override
public IContentFormatter getContentFormatter(ISourceViewer sourceViewer) {
final String natureId = getNatureId();
if (ScriptFormatterManager.hasFormatterFor(natureId)) {
final MultiPassContentFormatter formatter = new MultiPassContentFormatter(
getConfiguredDocumentPartitioning(sourceViewer),
IDocument.DEFAULT_CONTENT_TYPE);
formatter.setMasterStrategy(new ScriptFormattingStrategy(natureId));
return formatter;
}
return super.getContentFormatter(sourceViewer);
}
/*
* @see SourceViewerConfiguration#getIndentPrefixes(ISourceViewer, String)
*/
@Override
public String[] getIndentPrefixes(ISourceViewer sourceViewer,
String contentType) {
if (fPreferenceStore == null) {
return super.getIndentPrefixes(sourceViewer, contentType);
}
final TabStyle tabStyle = getTabStyle();
final int tabWidth = getTabWidth(sourceViewer);
final int indentWidth = getIndentationSize(sourceViewer);
if (tabStyle != TabStyle.TAB && indentWidth < tabWidth) {
return new String[] { AutoEditUtils.getNSpaces(indentWidth), "\t", //$NON-NLS-1$
Util.EMPTY_STRING };
} else if (tabStyle == TabStyle.TAB) {
return getIndentPrefixesForTab(tabWidth);
} else {
return getIndentPrefixesForSpaces(tabWidth);
}
}
protected TabStyle getTabStyle() {
if (fPreferenceStore != null) {
TabStyle tabStyle = TabStyle.forName(fPreferenceStore
.getString(CodeFormatterConstants.FORMATTER_TAB_CHAR));
if (tabStyle != null) {
return tabStyle;
}
}
return TabStyle.TAB;
}
@Override
public int getTabWidth(ISourceViewer sourceViewer) {
if (fPreferenceStore == null)
return super.getTabWidth(sourceViewer);
return fPreferenceStore
.getInt(CodeFormatterConstants.FORMATTER_TAB_SIZE);
}
protected int getIndentationSize(ISourceViewer sourceViewer) {
if (fPreferenceStore == null)
return super.getTabWidth(sourceViewer);
return fPreferenceStore
.getInt(CodeFormatterConstants.FORMATTER_INDENTATION_SIZE);
}
/**
* Computes and returns the indent prefixes for space indentation and the
* given <code>tabWidth</code>.
*
* @param tabWidth
* the display tab width
* @return the indent prefixes
* @see #getIndentPrefixes(ISourceViewer, String)
*/
protected String[] getIndentPrefixesForSpaces(int tabWidth) {
final String[] indentPrefixes = new String[tabWidth + 2];
indentPrefixes[0] = AutoEditUtils.getNSpaces(tabWidth);
for (int i = 0; i < tabWidth; i++) {
indentPrefixes[i + 1] = AutoEditUtils.getNSpaces(i) + '\t';
}
indentPrefixes[tabWidth + 1] = ""; //$NON-NLS-1$
return indentPrefixes;
}
/*
* @see
* SourceViewerConfiguration#getInformationControlCreator(ISourceViewer)
*
* @since 3.0
*/
@Override
public IInformationControlCreator getInformationControlCreator(
ISourceViewer sourceViewer) {
return parent -> new DefaultInformationControl(parent, false);
}
/*
* @see SourceViewerConfiguration#getAnnotationHover(ISourceViewer)
*
* @since 3.0
*/
@Override
public IAnnotationHover getAnnotationHover(ISourceViewer sourceViewer) {
return new HTMLAnnotationHover(false) {
@Override
protected boolean isIncluded(Annotation annotation) {
return isShowInVerticalRuler(annotation);
}
};
}
/*
* @see
* SourceViewerConfiguration#getOverviewRulerAnnotationHover(ISourceViewer)
*
* @since 3.0
*/
@Override
public IAnnotationHover getOverviewRulerAnnotationHover(
ISourceViewer sourceViewer) {
return new HTMLAnnotationHover(true) {
@Override
protected boolean isIncluded(Annotation annotation) {
return isShowInOverviewRuler(annotation);
}
};
}
protected SpellCheckDelegate createSpellCheckDelegate() {
return new SpellCheckDelegate();
}
@Override
public IQuickAssistAssistant getQuickAssistAssistant(
ISourceViewer sourceViewer) {
if (getEditor() != null)
return new ScriptCorrectionAssistant(getEditor(), fPreferenceStore,
getColorManager());
return super.getQuickAssistAssistant(sourceViewer);
}
}