/*******************************************************************************
* Copyright (c) 2009 IBM Corporation.
* 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
*
* Contributors:
* Robert Fuhrer (rfuhrer@watson.ibm.com) - initial API and implementation
*******************************************************************************/
package org.eclipse.imp.editor;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.eclipse.imp.editor.internal.QuickFixController;
import org.eclipse.imp.parser.IParseController;
import org.eclipse.imp.parser.ISourcePositionLocator;
import org.eclipse.imp.preferences.IPreferencesService;
import org.eclipse.imp.preferences.PreferenceCache;
import org.eclipse.imp.preferences.PreferenceConstants;
import org.eclipse.imp.runtime.RuntimePlugin;
import org.eclipse.imp.services.IDocumentationProvider;
import org.eclipse.imp.services.base.DefaultAnnotationHover;
import org.eclipse.imp.services.base.TreeModelBuilderBase;
import org.eclipse.imp.ui.textPresentation.HTMLTextPresenter;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.*;
import org.eclipse.jface.text.contentassist.ContentAssistant;
import org.eclipse.jface.text.contentassist.IContentAssistant;
import org.eclipse.jface.text.formatter.ContentFormatter;
import org.eclipse.jface.text.formatter.IContentFormatter;
import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
import org.eclipse.jface.text.hyperlink.IHyperlinkPresenter;
import org.eclipse.jface.text.information.IInformationPresenter;
import org.eclipse.jface.text.information.IInformationProvider;
import org.eclipse.jface.text.information.IInformationProviderExtension;
import org.eclipse.jface.text.information.InformationPresenter;
import org.eclipse.jface.text.presentation.IPresentationReconciler;
import org.eclipse.jface.text.presentation.PresentationReconciler;
import org.eclipse.jface.text.quickassist.IQuickAssistAssistant;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.IAnnotationHover;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.editors.text.TextSourceViewerConfiguration;
public class StructuredSourceViewerConfiguration extends TextSourceViewerConfiguration {
protected final UniversalEditor fEditor;
private ServiceControllerManager fServiceControllerManager;
private LanguageServiceManager fLanguageServiceManager;
private IPreferencesService fLangSpecificPrefs;
public StructuredSourceViewerConfiguration(IPreferenceStore prefStore, UniversalEditor editor) {
super(prefStore);
fEditor= editor;
// Can't cache the ServiceControllerManager, LangaugeServiceManager, or the IPreferencesService
// yet, b/c they haven't been set up by the editor yet. Retrieve them lazily.
}
protected ServiceControllerManager getServiceControllerManager() {
if (fServiceControllerManager == null) {
fServiceControllerManager= fEditor.fServiceControllerManager;
}
return fServiceControllerManager;
}
protected LanguageServiceManager getLanguageServiceManager() {
if (fLanguageServiceManager == null) {
fLanguageServiceManager= fEditor.getLanguageServiceManager();
}
return fLanguageServiceManager;
}
protected IPreferencesService getLangSpecificPrefs() {
if (fLangSpecificPrefs == null) {
fLangSpecificPrefs= fEditor.getLanguageSpecificPreferences();
}
return fLangSpecificPrefs;
}
public int getTabWidth(ISourceViewer sourceViewer) {
boolean langSpecificSetting= getLangSpecificPrefs() != null && getLangSpecificPrefs().isDefined(PreferenceConstants.P_TAB_WIDTH);
return langSpecificSetting ? getLangSpecificPrefs().getIntPreference(PreferenceConstants.P_TAB_WIDTH) : PreferenceCache.tabWidth;
}
public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) {
if (getServiceControllerManager() == null || getLanguageServiceManager().getTokenColorer() == null) {
return super.getPresentationReconciler(sourceViewer);
}
// BUG Perhaps we shouldn't use a PresentationReconciler; its JavaDoc says it runs in the UI thread!
PresentationReconciler reconciler= new PresentationReconciler();
reconciler.setRepairer(fEditor.new PresentationRepairer(), IDocument.DEFAULT_CONTENT_TYPE);
reconciler.setDamager(fEditor.new PresentationDamager(), IDocument.DEFAULT_CONTENT_TYPE);
return reconciler;
}
public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) {
if (getServiceControllerManager() == null) {
return super.getContentAssistant(sourceViewer);
}
ContentAssistant ca= new ContentAssistant();
ca.setContentAssistProcessor(getServiceControllerManager().getCompletionProcessor(), IDocument.DEFAULT_CONTENT_TYPE);
ca.setInformationControlCreator(getInformationControlCreator(sourceViewer));
return ca;
}
public IAnnotationHover getAnnotationHover(ISourceViewer sourceViewer) {
if (getLanguageServiceManager() == null) {
return super.getAnnotationHover(sourceViewer);
}
IAnnotationHover hover= getLanguageServiceManager().getAnnotationHover();
if (hover == null)
hover= new DefaultAnnotationHover();
return hover;
}
public IAutoEditStrategy[] getAutoEditStrategies(ISourceViewer sourceViewer, String contentType) {
if (getLanguageServiceManager() == null) {
return super.getAutoEditStrategies(sourceViewer, contentType);
}
Set<org.eclipse.imp.services.IAutoEditStrategy> autoEdits= getLanguageServiceManager().getAutoEditStrategies();
if (autoEdits == null || autoEdits.size() == 0) {
return super.getAutoEditStrategies(sourceViewer, contentType);
}
return autoEdits.toArray(new IAutoEditStrategy[autoEdits.size()]);
}
public IContentFormatter getContentFormatter(ISourceViewer sourceViewer) {
// Disable the content formatter if no language-specific implementation exists.
// N.B.: This will probably always be null, since this method gets called before
// the formatting controller has been instantiated (which happens in
// instantiateServiceControllers()).
if (getServiceControllerManager() == null || getServiceControllerManager().getFormattingController() == null)
return null;
// For now, assumes only one content type (i.e. one kind of partition)
ContentFormatter formatter= new ContentFormatter();
formatter.setFormattingStrategy(getServiceControllerManager().getFormattingController(), IDocument.DEFAULT_CONTENT_TYPE);
return formatter;
}
public String[] getDefaultPrefixes(ISourceViewer sourceViewer, String contentType) {
return super.getDefaultPrefixes(sourceViewer, contentType);
}
public ITextDoubleClickStrategy getDoubleClickStrategy(ISourceViewer sourceViewer, String contentType) {
LanguageServiceManager lsm= getLanguageServiceManager();
return (lsm != null) ? new DoubleClickStrategy(lsm.getParseController()) : super.getDoubleClickStrategy(sourceViewer, contentType);
}
public IHyperlinkDetector[] getHyperlinkDetectors(ISourceViewer sourceViewer) {
if (getServiceControllerManager() != null && getServiceControllerManager().getHyperLinkController() != null)
return new IHyperlinkDetector[] { getServiceControllerManager().getHyperLinkController() };
return super.getHyperlinkDetectors(sourceViewer);
}
public IHyperlinkPresenter getHyperlinkPresenter(ISourceViewer sourceViewer) {
return super.getHyperlinkPresenter(sourceViewer);
}
public String[] getIndentPrefixes(ISourceViewer sourceViewer, String contentType) {
return super.getIndentPrefixes(sourceViewer, contentType);
}
/**
* Used to present hover help (anything else?)
*/
public IInformationControlCreator getInformationControlCreator(ISourceViewer sourceViewer) {
return new IInformationControlCreator() {
public IInformationControl createInformationControl(Shell parent) {
return new DefaultInformationControl(parent, "Press 'F2' for focus", new HTMLTextPresenter(true));
}
};
}
private InformationPresenter fInfoPresenter;
public IInformationPresenter getInformationPresenter(ISourceViewer sourceViewer) {
if (getLanguageServiceManager() == null) {
return super.getInformationPresenter(sourceViewer);
}
if (fInfoPresenter == null) {
fInfoPresenter= new InformationPresenter(getInformationControlCreator(sourceViewer));
fInfoPresenter.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer));
fInfoPresenter.setAnchor(AbstractInformationControlManager.ANCHOR_GLOBAL);
IInformationProvider provider= new IInformationProvider() {
private IAnnotationModel fAnnotationModel= fEditor.getDocumentProvider().getAnnotationModel(fEditor.getEditorInput());
private List<Annotation> getParserAnnotationsAtOffset(int offset) {
List<Annotation> result= new LinkedList<Annotation>();
if (fAnnotationModel != null) {
for(Iterator<Annotation> iter= fAnnotationModel.getAnnotationIterator(); iter.hasNext(); ) {
Annotation ann= iter.next();
if (fAnnotationModel.getPosition(ann).includes(offset) && UniversalEditor.isParseAnnotation(ann)) {
result.add(ann);
}
}
}
return result;
}
public IRegion getSubject(ITextViewer textViewer, int offset) {
List<Annotation> parserAnnsAtOffset = getParserAnnotationsAtOffset(offset);
if (parserAnnsAtOffset.size() > 0) {
Annotation theAnn= parserAnnsAtOffset.get(0);
Position pos= fAnnotationModel.getPosition(theAnn);
return new Region(pos.offset, pos.length);
}
IParseController pc= getLanguageServiceManager().getParseController();
ISourcePositionLocator locator= pc.getSourcePositionLocator();
if (locator == null) {
return new Region(offset, 0);
}
Object selNode= locator.findNode(pc.getCurrentAst(), offset);
return new Region(locator.getStartOffset(selNode), locator.getLength(selNode));
}
public String getInformation(ITextViewer textViewer, IRegion subject) {
List<Annotation> parserAnnsAtOffset = getParserAnnotationsAtOffset(subject.getOffset());
if (parserAnnsAtOffset.size() > 0) {
Annotation theAnn= parserAnnsAtOffset.get(0);
return theAnn.getText();
}
IParseController pc= getLanguageServiceManager().getParseController();
ISourcePositionLocator locator= pc.getSourcePositionLocator();
if (locator == null) {
return "";
}
IDocumentationProvider docProvider= getLanguageServiceManager().getDocProvider();
Object selNode= locator.findNode(pc.getCurrentAst(), subject.getOffset());
return (docProvider != null) ? docProvider.getDocumentation(selNode, pc) : null;
}
};
fInfoPresenter.setInformationProvider(provider, IDocument.DEFAULT_CONTENT_TYPE);
fInfoPresenter.setSizeConstraints(60, 10, true, false);
fInfoPresenter.setRestoreInformationControlBounds(getSettings("outline_presenter_bounds"), true, true); //$NON-NLS-1$
}
return fInfoPresenter;
}
public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType) {
if (getServiceControllerManager() == null) {
return super.getTextHover(sourceViewer, contentType);
}
return getServiceControllerManager().getHoverHelpController();
}
public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType, int stateMask) {
return super.getTextHover(sourceViewer, contentType, stateMask);
}
public IUndoManager getUndoManager(ISourceViewer sourceViewer) {
return super.getUndoManager(sourceViewer);
}
public IAnnotationHover getOverviewRulerAnnotationHover(ISourceViewer sourceViewer) {
return super.getOverviewRulerAnnotationHover(sourceViewer);
}
private class OutlineInformationProvider implements IInformationProvider, IInformationProviderExtension {
private TreeModelBuilderBase fBuilder;
public IRegion getSubject(ITextViewer textViewer, int offset) {
return new Region(offset, 0); // Could be anything, since it's ignored below in getInformation2()...
}
public String getInformation(ITextViewer textViewer, IRegion subject) {
return "never called?!?"; // shouldn't be called, given IInformationProviderExtension???
}
public Object getInformation2(ITextViewer textViewer, IRegion subject) {
if (fBuilder == null) {
fBuilder= getLanguageServiceManager().getModelBuilder();
}
return fBuilder.buildTree(getLanguageServiceManager().getParseController().getCurrentAst());
}
}
private IInformationProvider fOutlineElementProvider= new OutlineInformationProvider();
public IInformationPresenter getOutlinePresenter(ISourceViewer sourceViewer) {
if (getLanguageServiceManager() == null) {
return null;
}
if (getLanguageServiceManager().getModelBuilder() == null) {
return null;
}
InformationPresenter presenter;
presenter= new InformationPresenter(getOutlinePresenterControlCreator(sourceViewer, IEditorActionDefinitionIds.SHOW_OUTLINE));
presenter.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer));
presenter.setAnchor(AbstractInformationControlManager.ANCHOR_GLOBAL);
IInformationProvider provider= fOutlineElementProvider;
presenter.setInformationProvider(provider, IDocument.DEFAULT_CONTENT_TYPE);
// TODO Should associate all other partition types with this provider, too
presenter.setSizeConstraints(50, 20, true, false);
presenter.setRestoreInformationControlBounds(getSettings("outline_presenter_bounds"), true, true); //$NON-NLS-1$
return presenter;
}
/**
* 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>OutlineInformationControl</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
*/
private IInformationControlCreator getOutlinePresenterControlCreator(ISourceViewer sourceViewer, final String commandId) {
return new IInformationControlCreator() {
public IInformationControl createInformationControl(Shell parent) {
int shellStyle= SWT.RESIZE;
int treeStyle= SWT.V_SCROLL | SWT.H_SCROLL;
return new OutlineInformationControl(parent, shellStyle, treeStyle, commandId, fEditor.getLanguage());
}
};
}
/**
* Returns the hierarchy presenter which will determine and shown type hierarchy information requested for the
* current cursor position.
*
* @param sourceViewer
* the source viewer to be configured by this configuration
* @param doCodeResolve
* a boolean which specifies whether code resolve should be used to compute the program element
* @return an information presenter
*/
public IInformationPresenter getHierarchyPresenter(ISourceViewer sourceViewer, boolean doCodeResolve) {
InformationPresenter presenter= new InformationPresenter(getHierarchyPresenterControlCreator(sourceViewer));
presenter.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer));
presenter.setAnchor(AbstractInformationControlManager.ANCHOR_GLOBAL);
IInformationProvider provider= null; // TODO RMF new HierarchyInformationProvider(this);
presenter.setInformationProvider(provider, IDocument.DEFAULT_CONTENT_TYPE);
// presenter.setInformationProvider(provider, IJavaPartitions.JAVA_DOC);
// presenter.setInformationProvider(provider, IJavaPartitions.JAVA_MULTI_LINE_COMMENT);
// presenter.setInformationProvider(provider, IJavaPartitions.JAVA_SINGLE_LINE_COMMENT);
// presenter.setInformationProvider(provider, IJavaPartitions.JAVA_STRING);
// presenter.setInformationProvider(provider, IJavaPartitions.JAVA_CHARACTER);
presenter.setSizeConstraints(50, 20, true, false);
presenter.setRestoreInformationControlBounds(getSettings("hierarchy_presenter_bounds"), true, true); //$NON-NLS-1$
return presenter;
}
private IInformationControlCreator getHierarchyPresenterControlCreator(ISourceViewer sourceViewer) {
return new IInformationControlCreator() {
public IInformationControl createInformationControl(Shell parent) {
// int shellStyle= SWT.RESIZE;
// int treeStyle= SWT.V_SCROLL | SWT.H_SCROLL;
return new DefaultInformationControl(parent); // HierarchyInformationControl(parent, shellStyle,
// treeStyle);
}
};
}
/**
* Returns the settings for the given section.
*
* @param sectionName
* the section name
* @return the settings
* @since 3.0
*/
private IDialogSettings getSettings(String sectionName) {
IDialogSettings settings= RuntimePlugin.getInstance().getDialogSettings().getSection(sectionName);
if (settings == null)
settings= RuntimePlugin.getInstance().getDialogSettings().addNewSection(sectionName);
return settings;
}
@Override
public IQuickAssistAssistant getQuickAssistAssistant(ISourceViewer sourceViewer) {
return new QuickFixController(fEditor);
}
}