/******************************************************************************* * Copyright (c) 2006 Business Objects Software Limited and others. * All rights reserved. * This file is 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: * Business Objects Software Limited - initial API and implementation based on Eclipse 3.1.2 code for * /org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/text/JavaSourceViewerConfiguration.java * Eclipse source is available at: http://www.eclipse.org/downloads/ *******************************************************************************/ /* * CALSourceViewerConfiguration.java * Creation date: Feb 1, 2006. * By: Edward Lam */ package org.openquark.cal.eclipse.ui.text; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.Vector; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IStorage; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IStatus; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.AbstractInformationControlManager; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.DefaultInformationControl; import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IAutoEditStrategy; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IInformationControl; import org.eclipse.jface.text.IInformationControlCreator; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextDoubleClickStrategy; import org.eclipse.jface.text.ITextHover; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.ITextViewerExtension2; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.contentassist.ContentAssistant; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; import org.eclipse.jface.text.contentassist.IContentAssistant; import org.eclipse.jface.text.contentassist.IContextInformation; import org.eclipse.jface.text.formatter.IContentFormatter; 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.IInformationProviderExtension; import org.eclipse.jface.text.information.IInformationProviderExtension2; 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.quickassist.IQuickAssistInvocationContext; import org.eclipse.jface.text.quickassist.IQuickAssistProcessor; import org.eclipse.jface.text.quickassist.QuickAssistAssistant; import org.eclipse.jface.text.reconciler.IReconciler; import org.eclipse.jface.text.rules.DefaultDamagerRepairer; import org.eclipse.jface.text.rules.RuleBasedScanner; 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.jface.util.PropertyChangeEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.IPartListener; import org.eclipse.ui.IPerspectiveDescriptor; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.editors.text.TextSourceViewerConfiguration; import org.eclipse.ui.texteditor.ITextEditor; import org.eclipse.ui.texteditor.MarkerAnnotation; import org.openquark.cal.compiler.CompilerMessageLogger; import org.openquark.cal.compiler.MessageLogger; import org.openquark.cal.compiler.ModuleName; import org.openquark.cal.compiler.SourcePosition; import org.openquark.cal.eclipse.core.CALEclipseCorePlugin; import org.openquark.cal.eclipse.core.CALModelManager; import org.openquark.cal.eclipse.core.CoreOptionIDs; import org.openquark.cal.eclipse.core.builder.CALBuilder; import org.openquark.cal.eclipse.core.builder.CALBuilder.IQuickFix; import org.openquark.cal.eclipse.core.formatter.DefaultCodeFormatterConstants; import org.openquark.cal.eclipse.ui.actions.ActionMessages; import org.openquark.cal.eclipse.ui.actions.CALEditorActionDefinitionIds; import org.openquark.cal.eclipse.ui.caleditor.CALDocumentProvider; import org.openquark.cal.eclipse.ui.caleditor.CALEditor; import org.openquark.cal.eclipse.ui.caleditor.CALHyperlinkDetector; import org.openquark.cal.eclipse.ui.caleditor.CALEditor.AdaptedSourceViewer; import org.openquark.cal.eclipse.ui.text.cal.CALAutoIndentStrategy; import org.openquark.cal.eclipse.ui.text.cal.CALCodeScanner; import org.openquark.cal.eclipse.ui.text.cal.CALDocDoubleClickStrategy; import org.openquark.cal.eclipse.ui.text.cal.CALDoubleClickSelector; import org.openquark.cal.eclipse.ui.text.cal.CALStringDoubleClickSelector; import org.openquark.cal.eclipse.ui.text.caldoc.CALDocAutoIndentStrategy; import org.openquark.cal.eclipse.ui.text.caldoc.CALDocScanner; import org.openquark.cal.eclipse.ui.util.CodeFormatterUtil; import org.openquark.cal.eclipse.ui.util.CoreUtility; import org.openquark.cal.eclipse.ui.views.CALOutlineInformationControl; import org.openquark.util.UnsafeCast; /** * Configuration for a source viewer which shows Java code. * <p> * This class may be instantiated; it is not intended to be subclassed. * </p> */ public class CALSourceViewerConfiguration extends TextSourceViewerConfiguration { private CALTextTools fCALTextTools; ITextEditor fTextEditor; /** * The document partitioning. */ private final String fDocumentPartitioning; /** * The Java source code scanner. */ private AbstractCALScanner fCodeScanner; /** * The Java multi-line comment scanner. */ private AbstractCALScanner fMultilineCommentScanner; /** * The Java single-line comment scanner. */ private AbstractCALScanner fSinglelineCommentScanner; /** * The Java string scanner. */ private AbstractCALScanner fStringScanner; /** * The Javadoc scanner. */ private AbstractCALScanner fCALDocScanner; /** * The color manager. */ private final ColorManager fColorManager; /** * The double click strategy. */ private CALDoubleClickSelector fCALDoubleClickSelector; private final ContentAssistant assistant = new ContentAssistant(); /** * Creates a new Java source viewer configuration for viewers in the given editor * using the given preference store, the color manager and the specified document partitioning. * <p> * Creates a Java source viewer configuration in the new setup without text tools. Clients are * allowed to call {@link CALSourceViewerConfiguration#handlePropertyChangeEvent(PropertyChangeEvent)} * and disallowed to call {@link CALSourceViewerConfiguration#getPreferenceStore()} on the resulting * Java source viewer configuration. * </p> * * @param colorManager the color manager * @param preferenceStore the preference store, can be read-only * @param editor the editor in which the configured viewer(s) will reside, or <code>null</code> if none * @param partitioning the document partitioning for this configuration, or <code>null</code> for the default partitioning */ public CALSourceViewerConfiguration(ColorManager colorManager, IPreferenceStore preferenceStore, ITextEditor editor, String partitioning) { super(preferenceStore); fColorManager = colorManager; fTextEditor = editor; fDocumentPartitioning = partitioning; initializeScanners(); } /** * Returns the Java source code scanner for this configuration. * * @return the Java source code scanner */ protected RuleBasedScanner getCodeScanner() { return fCodeScanner; } /** * Returns the Java multi-line comment scanner for this configuration. * * @return the Java multi-line comment scanner */ protected RuleBasedScanner getMultilineCommentScanner() { return fMultilineCommentScanner; } /** * Returns the Java single-line comment scanner for this configuration. * * @return the Java single-line comment scanner */ protected RuleBasedScanner getSinglelineCommentScanner() { return fSinglelineCommentScanner; } /** * Returns the Java string scanner for this configuration. * * @return the Java string scanner */ protected RuleBasedScanner getStringScanner() { return fStringScanner; } /** * Returns the JavaDoc scanner for this configuration. * * @return the JavaDoc scanner */ protected RuleBasedScanner getJavaDocScanner() { return fCALDocScanner; } /** * Returns the color manager for this configuration. * * @return the color manager */ protected ColorManager getColorManager() { return fColorManager; } /** * Returns the editor in which the configured viewer(s) will reside. * * @return the enclosing editor */ protected ITextEditor getEditor() { return fTextEditor; } /** * Returns the preference store used by this configuration to initialize * the individual bits and pieces. * <p> * Clients are not allowed to call this method if the new setup without * text tools is in use. * @see CALSourceViewerConfiguration#CALSourceViewerConfiguration(ColorManager, IPreferenceStore, ITextEditor, String) * </p> * * @return the preference store used to initialize this configuration * @deprecated As of 3.0 */ @Deprecated protected IPreferenceStore getPreferenceStore() { Assert.isTrue(!isNewSetup()); return fCALTextTools.getPreferenceStore(); } /** * @return <code>true</code> iff the new setup without text tools is in use. */ private boolean isNewSetup() { return fCALTextTools == null; } // /** // * Creates and returns a preference store which combines the preference // * stores from the text tools and which is read-only. // * // * @param javaTextTools the Java text tools // * @return the combined read-only preference store // */ // private static final IPreferenceStore createPreferenceStore(CALTextTools javaTextTools) { // Assert.isNotNull(javaTextTools); // IPreferenceStore generalTextStore= EditorsUI.getPreferenceStore(); // if (javaTextTools.getCorePreferenceStore() == null) // return new ChainedPreferenceStore(new IPreferenceStore[] { javaTextTools.getPreferenceStore(), generalTextStore}); // // return new ChainedPreferenceStore(new IPreferenceStore[] { javaTextTools.getPreferenceStore(), new PreferencesAdapter(javaTextTools.getCorePreferenceStore()), generalTextStore }); // } /** * Initializes the scanners. */ private void initializeScanners() { Assert.isTrue(isNewSetup()); fCodeScanner = new CALCodeScanner(getColorManager(), fPreferenceStore); fMultilineCommentScanner = new CALCommentScanner(getColorManager(), fPreferenceStore, CALColorConstants.CAL_MULTI_LINE_COMMENT); fSinglelineCommentScanner = new CALCommentScanner(getColorManager(), fPreferenceStore, CALColorConstants.CAL_SINGLE_LINE_COMMENT); fStringScanner = new SingleTokenCALScanner(getColorManager(), fPreferenceStore, CALColorConstants.CAL_STRING); fCALDocScanner = new CALDocScanner(getColorManager(), fPreferenceStore); } /* * @see SourceViewerConfiguration#getPresentationReconciler(ISourceViewer) */ @Override public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) { PresentationReconciler reconciler = new CALPresentationReconciler(); reconciler.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer)); DefaultDamagerRepairer dr = new DefaultDamagerRepairer(getCodeScanner()); reconciler.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE); reconciler.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE); dr = new DefaultDamagerRepairer(getJavaDocScanner()); reconciler.setDamager(dr, CALPartitions.CAL_DOC); reconciler.setRepairer(dr, CALPartitions.CAL_DOC); dr = new DefaultDamagerRepairer(getMultilineCommentScanner()); reconciler.setDamager(dr, CALPartitions.CAL_MULTI_LINE_COMMENT); reconciler.setRepairer(dr, CALPartitions.CAL_MULTI_LINE_COMMENT); dr = new DefaultDamagerRepairer(getSinglelineCommentScanner()); reconciler.setDamager(dr, CALPartitions.CAL_SINGLE_LINE_COMMENT); reconciler.setRepairer(dr, CALPartitions.CAL_SINGLE_LINE_COMMENT); dr = new DefaultDamagerRepairer(getStringScanner()); reconciler.setDamager(dr, CALPartitions.CAL_STRING); reconciler.setRepairer(dr, CALPartitions.CAL_STRING); dr = new DefaultDamagerRepairer(getStringScanner()); reconciler.setDamager(dr, CALPartitions.CAL_CHARACTER); reconciler.setRepairer(dr, CALPartitions.CAL_CHARACTER); return reconciler; } /* * @see SourceViewerConfiguration#getContentAssistant(ISourceViewer) */ @Override public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) { /* * TODOEL */ if (getEditor() != null) { CompletionProcessor completionProcessor = getCompletionProcessor(); if (completionProcessor != null) { assistant.setContentAssistProcessor(completionProcessor, IDocument.DEFAULT_CONTENT_TYPE); assistant.enableAutoActivation(true); assistant.enableAutoInsert(true); } return assistant; } return null; } /** * Returns whether the suffix is a suffix of this module name * * For example, "Prelude" is a suffix of the name "Cal.Core.Prelude", or * "Core.Prelude" is a suffix of "Cal.Core.Prelude". * * @param suffix The module name that might be a suffix of this name. * @param lastNameIsPartial The last name of the suffix is only partially entered. * @return true if the suffix is a suffix of this module name. */ public static boolean isSuffixOf(final ModuleName name, final String[] suffix, boolean lastNameIsPartial) { int i_suffix = suffix.length - 1; int i_prefix = name.getNComponents() - 1; if (i_suffix > i_prefix) { return false; } if (lastNameIsPartial){ if (!name.getNthComponent(i_prefix--).startsWith(suffix[i_suffix--])){ return false; } } while(i_suffix >= 0){ if (!suffix[i_suffix--].equals(name.getNthComponent(i_prefix--))){ return false; } } return true; } /** * Returns whether the suffix is a suffix of this module name * * For example, "Core" is the middle of "Cal.Core.Prelude". * * @param middle The module name that might be a suffix of this name. * @param lastNameIsPartial The last name of the suffix is only partially entered. * @return true if the suffix is a suffix of this module name. */ public static boolean isMiddleOf(final ModuleName name, final String[] middle, boolean lastNameIsPartial) { // Search for the first matching component int i_prefix = 0; boolean foundMatch = false; for(; i_prefix < name.getNComponents(); ++i_prefix){ if (lastNameIsPartial && middle.length == 1){ if (name.getNthComponent(i_prefix).startsWith(middle[0])){ foundMatch = true; break; } } else{ if (middle[0].equals(name.getNthComponent(i_prefix))){ foundMatch = true; break; } } } if (!foundMatch){ return false; } // match the rest of the components int i_middle = 1; i_prefix++; while(true){ if (i_middle >= middle.length){ return true; } if (i_prefix >= name.getNComponents()){ return false; } // if at the last name then partial matches are allowed if (lastNameIsPartial && i_middle == middle.length - 1){ if (!name.getNthComponent(i_prefix++).startsWith(middle[i_middle++])){ return false; } } else{ if (!middle[i_middle++].equals(name.getNthComponent(i_prefix++))){ return false; } } } } /** * Get the completion processor for the current editor. * @return the completion processor for the current editor, or null if none applies. */ private CompletionProcessor getCompletionProcessor() { // ensure that we are working with a CALEditor and not an // embedded editor (see CAL_Eclipse_EmbeddedEditor project) ITextEditor textEditor = getEditor(); if (textEditor == null || !(textEditor instanceof CALEditor)) { return null; } final CALEditor calEditor = (CALEditor) textEditor; if (calEditor.getEditorInput() instanceof IFileEditorInput) { try{ return new CompletionProcessor(calEditor, fPreferenceStore); } catch(IllegalArgumentException e){ // The name of the file does not // correspond to a valid module name. return null; } } return null; } /* * @see SourceViewerConfiguration#getReconciler(ISourceViewer) */ @Override public IReconciler getReconciler(ISourceViewer sourceViewer) { return null; } /* * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getAutoEditStrategies(org.eclipse.jface.text.source.ISourceViewer, java.lang.String) */ @Override public IAutoEditStrategy[] getAutoEditStrategies(ISourceViewer sourceViewer, String contentType) { String partitioning = getConfiguredDocumentPartitioning(sourceViewer); if (CALPartitions.CAL_DOC.equals(contentType) || CALPartitions.CAL_MULTI_LINE_COMMENT.equals(contentType)) { return new IAutoEditStrategy[]{new CALDocAutoIndentStrategy(partitioning)}; } // else if (CALPartitions.CAL_STRING.equals(contentType)) // return new IAutoEditStrategy[]{new SmartSemicolonAutoEditStrategy(partitioning), new JavaStringAutoIndentStrategy(partitioning)}; // else if (CALPartitions.CAL_CHARACTER.equals(contentType) || IDocument.DEFAULT_CONTENT_TYPE.equals(contentType)) // return new IAutoEditStrategy[]{new SmartSemicolonAutoEditStrategy(partitioning), new JavaAutoIndentStrategy(partitioning, getProject())}; // else // return new IAutoEditStrategy[]{new JavaAutoIndentStrategy(partitioning, getProject())}; return new IAutoEditStrategy[] { // new DefaultIndentLineAutoEditStrategy() new CALAutoIndentStrategy(partitioning, getProject()) // does the wrong thing, eg. newline at end of an indented comment -> not indented }; } /* * @see SourceViewerConfiguration#getDoubleClickStrategy(ISourceViewer, String) */ @Override public ITextDoubleClickStrategy getDoubleClickStrategy(ISourceViewer sourceViewer, String contentType) { if (CALPartitions.CAL_DOC.equals(contentType)) { return new CALDocDoubleClickStrategy(); } if (CALPartitions.CAL_MULTI_LINE_COMMENT.equals(contentType) || CALPartitions.CAL_SINGLE_LINE_COMMENT.equals(contentType)) { return new DefaultTextDoubleClickStrategy(); } else if (CALPartitions.CAL_STRING.equals(contentType) || CALPartitions.CAL_CHARACTER.equals(contentType)) { return new CALStringDoubleClickSelector(getConfiguredDocumentPartitioning(sourceViewer)); } if (fCALDoubleClickSelector == null) { fCALDoubleClickSelector = new CALDoubleClickSelector(); } return fCALDoubleClickSelector; } /* * @see SourceViewerConfiguration#getDefaultPrefixes(ISourceViewer, String) */ @Override public String[] getDefaultPrefixes(ISourceViewer sourceViewer, String contentType) { return new String[] { "//", "" }; //$NON-NLS-1$ //$NON-NLS-2$ } /* * @see SourceViewerConfiguration#getIndentPrefixes(ISourceViewer, String) */ @Override public String[] getIndentPrefixes(ISourceViewer sourceViewer, String contentType) { Vector<String> vector = new Vector<String>(); // prefix[0] is either '\t' or ' ' x tabWidth, depending on useSpaces IProject project = getProject(); final int tabWidth = CodeFormatterUtil.getTabWidth(project); final int indentWidth = CodeFormatterUtil.getIndentWidth(project); int spaceEquivalents = Math.min(tabWidth, indentWidth); boolean useSpaces; // if (project == null) useSpaces = CoreOptionIDs.SPACE.equals(CALEclipseCorePlugin.getOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR)) || tabWidth > indentWidth; // else // useSpaces = CoreOptionIDs.SPACE.equals(project.getOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, true)) || tabWidth > indentWidth; for (int i = 0; i <= spaceEquivalents; i++) { StringBuilder prefix = new StringBuilder(); if (useSpaces) { for (int j = 0; j + i < spaceEquivalents; j++) { prefix.append(' '); } if (i != 0) { prefix.append('\t'); } } else { for (int j = 0; j < i; j++) { prefix.append(' '); } if (i != spaceEquivalents) { prefix.append('\t'); } } vector.add(prefix.toString()); } vector.add(""); //$NON-NLS-1$ return vector.toArray(new String[vector.size()]); } private IProject getProject() { return null; // ITextEditor editor= getEditor(); // if (editor == null) // return null; // // IJavaElement element= null; // IEditorInput input= editor.getEditorInput(); // IDocumentProvider provider= editor.getDocumentProvider(); // if (provider instanceof ICompilationUnitDocumentProvider) { // ICompilationUnitDocumentProvider cudp= (ICompilationUnitDocumentProvider) provider; // element= cudp.getWorkingCopy(input); // } else if (input instanceof IClassFileEditorInput) { // IClassFileEditorInput cfei= (IClassFileEditorInput) input; // element= cfei.getClassFile(); // } // // if (element == null) // return null; // // return element.getJavaProject(); } /* * @see SourceViewerConfiguration#getTabWidth(ISourceViewer) */ @Override public int getTabWidth(ISourceViewer sourceViewer) { return CodeFormatterUtil.getTabWidth(getProject()); } /* * @see SourceViewerConfiguration#getAnnotationHover(ISourceViewer) */ @Override public IAnnotationHover getAnnotationHover(ISourceViewer sourceViewer) { return new CALAnnotationHover(CALAnnotationHover.VERTICAL_RULER_HOVER); } /* * @see SourceViewerConfiguration#getOverviewRulerAnnotationHover(ISourceViewer) */ @Override public IAnnotationHover getOverviewRulerAnnotationHover(ISourceViewer sourceViewer) { return new CALAnnotationHover(CALAnnotationHover.OVERVIEW_RULER_HOVER); } /** * * {@inheritDoc} */ @Override public int[] getConfiguredTextHoverStateMasks(ISourceViewer sourceViewer, String contentType) { return new int[] { ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK, SWT.SHIFT, // SWT.CONTROL }; } /* * @see SourceViewerConfiguration#getTextHover(ISourceViewer, String, int) */ @Override public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType, int stateMask) { // XXX Hack. We need to determine if we are being called from CALInformationProvider or from // the Source Viewer. In the former, we want the hover to be sticky (hence first argument is true. // In the latter we do not want the hover to stick. // We control this by looking at the first two arguments, which in the case of the CALInformationProvider // will always be null. return new CALTextHover(sourceViewer != null || contentType != null, stateMask, (CALEditor) getEditor()); } /* * @see SourceViewerConfiguration#getTextHover(ISourceViewer, String) */ @Override public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType) { return getTextHover(sourceViewer, contentType, ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK); } /* * @see SourceViewerConfiguration#getConfiguredContentTypes(ISourceViewer) */ @Override public String[] getConfiguredContentTypes(ISourceViewer sourceViewer) { return new String[] { IDocument.DEFAULT_CONTENT_TYPE, CALPartitions.CAL_DOC, CALPartitions.CAL_MULTI_LINE_COMMENT, CALPartitions.CAL_SINGLE_LINE_COMMENT, CALPartitions.CAL_STRING, CALPartitions.CAL_CHARACTER }; } /* * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getConfiguredDocumentPartitioning(org.eclipse.jface.text.source.ISourceViewer) */ @Override public String getConfiguredDocumentPartitioning(ISourceViewer sourceViewer) { if (fDocumentPartitioning != null) { return fDocumentPartitioning; } return super.getConfiguredDocumentPartitioning(sourceViewer); } /* * @see SourceViewerConfiguration#getContentFormatter(ISourceViewer) */ @Override public IContentFormatter getContentFormatter(ISourceViewer sourceViewer) { return null; } /** * Creates a hover control creator that is used for hovers over the * overview bar and other annotation hovers */ @Override public IInformationControlCreator getInformationControlCreator(ISourceViewer sourceViewer) { return new IInformationControlCreator() { public IInformationControl createInformationControl(Shell parent) { return new DefaultInformationControl(parent, SWT.NONE, new HTMLTextPresenter(true)); } }; } /* * @see SourceViewerConfiguration#getInformationPresenter(ISourceViewer) */ @Override public IInformationPresenter getInformationPresenter(ISourceViewer sourceViewer) { InformationPresenter presenter= new InformationPresenter(new IInformationControlCreator() { public IInformationControl createInformationControl(Shell parent) { int shellStyle= SWT.RESIZE | SWT.TOOL; int style= SWT.V_SCROLL | SWT.H_SCROLL; return new DefaultInformationControl(parent, shellStyle, style, new HTMLTextPresenter(false)); } }); presenter.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer)); IInformationProvider provider= new CALInformationProvider(getEditor()); presenter.setInformationProvider(provider, IDocument.DEFAULT_CONTENT_TYPE); presenter.setSizeConstraints(60, 10, true, true); return presenter; } /** * This is the hook needed so that the tooltip shows in a dialog that has a helper string on the bottom * that says "Press 'F2' for focus." and other things. */ public class CALInformationProvider implements IInformationProvider, IInformationProviderExtension2 { /** * Needed so update is called and the current hover information is used (if any). */ class EditorWatcher implements IPartListener { /** * @see IPartListener#partOpened(IWorkbenchPart) */ public void partOpened(IWorkbenchPart part) { update(); } /** * @see IPartListener#partDeactivated(IWorkbenchPart) */ public void partDeactivated(IWorkbenchPart part) { } /** * @see IPartListener#partClosed(IWorkbenchPart) */ public void partClosed(IWorkbenchPart part) { if (part == fEditor) { fEditor.getSite().getWorkbenchWindow().getPartService().removePartListener(fPartListener); fPartListener= null; } } /** * @see IPartListener#partActivated(IWorkbenchPart) */ public void partActivated(IWorkbenchPart part) { update(); } public void partBroughtToTop(IWorkbenchPart part) { update(); } } protected IEditorPart fEditor; protected IPartListener fPartListener; protected String fCurrentPerspective; protected CALTextHover fImplementation; public CALInformationProvider(IEditorPart editor) { fEditor= editor; if (fEditor != null) { fPartListener= new EditorWatcher(); IWorkbenchWindow window= fEditor.getSite().getWorkbenchWindow(); window.getPartService().addPartListener(fPartListener); update(); } } protected void update() { IWorkbenchWindow window= fEditor.getSite().getWorkbenchWindow(); IWorkbenchPage page= window.getActivePage(); if (page != null) { IPerspectiveDescriptor perspective= page.getPerspective(); if (perspective != null) { String perspectiveId= perspective.getId(); if (fCurrentPerspective == null || !fCurrentPerspective.equals(perspectiveId)) { fCurrentPerspective= perspectiveId; fImplementation= (CALTextHover) getTextHover(null, null, ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK); } } } } /* * @see IInformationProvider#getSubject(ITextViewer, int) */ public IRegion getSubject(ITextViewer textViewer, int offset) { // just return the current position since the lookup code will determine // what symbol is selected. return new Region(offset, 1); } /* * @see IInformationProvider#getInformation(ITextViewer, IRegion) */ public String getInformation(ITextViewer textViewer, IRegion subject) { if (fImplementation != null) { String s= fImplementation.getHoverInfo(textViewer, subject); if (s != null && s.trim().length() > 0) { return s; } } return null; } /* * This is the hover that gets created when F2 is pressed on an existing hover * @see IInformationProviderExtension2#getInformationPresenterControlCreator() * @since 3.1 */ public IInformationControlCreator getInformationPresenterControlCreator() { return new IInformationControlCreator() { public IInformationControl createInformationControl(Shell parent) { int shellStyle= SWT.RESIZE | SWT.TOOL; int style= SWT.V_SCROLL | SWT.H_SCROLL; if (fImplementation.showInBrowser && BrowserInformationControl.isAvailable(parent)) { return new BrowserInformationControl(parent, shellStyle, style); } else { return new CALSourceInformationControl(parent, shellStyle, style); } } }; } } /** * Returns the outline presenter which will determine and shown * 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 Java element * @return an information presenter * @since 2.1 */ public IInformationPresenter getOutlinePresenter(ISourceViewer sourceViewer, boolean doCodeResolve) { InformationPresenter presenter; if (doCodeResolve) presenter= new InformationPresenter(getOutlinePresenterControlCreator(sourceViewer, CALEditorActionDefinitionIds.OPEN_STRUCTURE)); else presenter= new InformationPresenter(getOutlinePresenterControlCreator(sourceViewer, CALEditorActionDefinitionIds.SHOW_OUTLINE)); presenter.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer)); presenter.setAnchor(AbstractInformationControlManager.ANCHOR_GLOBAL); IInformationProvider provider= new CALElementProvider(getEditor(), doCodeResolve); final String partitions[] = { IDocument.DEFAULT_CONTENT_TYPE, fDocumentPartitioning, CALPartitions.CAL_PARTITIONING, CALPartitions.CAL_SINGLE_LINE_COMMENT, CALPartitions.CAL_MULTI_LINE_COMMENT, CALPartitions.CAL_DOC, CALPartitions.CAL_STRING, CALPartitions.CAL_CHARACTER}; for(String partition : partitions){ presenter.setInformationProvider(provider, partition); } presenter.setSizeConstraints(50, 20, true, false); return presenter; } public class CALElementProvider implements IInformationProvider, IInformationProviderExtension { private CALEditor fEditor; public CALElementProvider(IEditorPart editor) { if (editor instanceof CALEditor) { fEditor = (CALEditor)editor; } } public CALElementProvider(IEditorPart editor, boolean useCodeResolve) { this(editor); } /* * @see IInformationProvider#getSubject(ITextViewer, int) */ public IRegion getSubject(ITextViewer textViewer, int offset) { // if (textViewer != null && fEditor != null) { // IRegion region= JavaWordFinder.findWord(textViewer.getDocument(), offset); // if (region != null) // return region; // else // return new Region(offset, 0); // } return new Region(offset, 0); } /* * @see IInformationProvider#getInformation(ITextViewer, IRegion) */ public String getInformation(ITextViewer textViewer, IRegion subject) { return getInformation2(textViewer, subject).toString(); } /* * @see IInformationProviderExtension#getElement(ITextViewer, IRegion) */ public Object getInformation2(ITextViewer textViewer, IRegion subject) { if (fEditor == null) return null; if (textViewer instanceof CALEditor.AdaptedSourceViewer){ CALEditor.AdaptedSourceViewer asv = (AdaptedSourceViewer) textViewer; final IStorage storage = asv.getEditor().getStorage(); try{ CALModelManager cmm = CALModelManager.getCALModelManager(); return cmm.getModuleName(storage); } catch(IllegalArgumentException ex){ // CAL File is not in the correct spot in the hierarchy so there // is no type information available. CoreUtility.showMessage(ActionMessages.OpenDeclarationAction_error_title, ActionMessages.error_calFileNotInCorrectLocation_message, IStatus.ERROR); return null; } } return null; } } /** * 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 Java element * @return an information presenter */ public IInformationPresenter getHierarchyPresenter(ISourceViewer sourceViewer, boolean doCodeResolve) { // TODOEL return null; } /** * Determines whether the preference change encoded by the given event * changes the behavior of one of its contained components. * * @param event the event to be investigated * @return <code>true</code> if event causes a behavioral change */ public boolean affectsTextPresentation(PropertyChangeEvent event) { return fCodeScanner.affectsBehavior(event) || fMultilineCommentScanner.affectsBehavior(event) || fSinglelineCommentScanner.affectsBehavior(event) || fStringScanner.affectsBehavior(event) || fCALDocScanner.affectsBehavior(event); } /** * Adapts the behavior of the contained components to the change * encoded in the given event. * <p> * Clients are not allowed to call this method if the old setup with * text tools is in use. * </p> * * @param event the event to which to adapt * @see CALSourceViewerConfiguration#CALSourceViewerConfiguration(ColorManager, IPreferenceStore, ITextEditor, String) */ public void handlePropertyChangeEvent(PropertyChangeEvent event) { Assert.isTrue(isNewSetup()); if (fCodeScanner.affectsBehavior(event)) { fCodeScanner.adaptToPreferenceChange(event); } if (fMultilineCommentScanner.affectsBehavior(event)) { fMultilineCommentScanner.adaptToPreferenceChange(event); } if (fSinglelineCommentScanner.affectsBehavior(event)) { fSinglelineCommentScanner.adaptToPreferenceChange(event); } if (fStringScanner.affectsBehavior(event)) { fStringScanner.adaptToPreferenceChange(event); } if (fCALDocScanner.affectsBehavior(event)) { fCALDocScanner.adaptToPreferenceChange(event); } } /* * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getHyperlinkDetectors(org.eclipse.jface.text.source.ISourceViewer) */ @Override public IHyperlinkDetector[] getHyperlinkDetectors(ISourceViewer sourceViewer) { IHyperlinkDetector[] hyperlinkDetectors = super.getHyperlinkDetectors(sourceViewer); ArrayList<IHyperlinkDetector> newHD = new ArrayList<IHyperlinkDetector>(); for (IHyperlinkDetector hd : hyperlinkDetectors){ newHD.add(hd); } newHD.add(new CALHyperlinkDetector()); return newHD.toArray(new IHyperlinkDetector[newHD.size()]); } public class QuickAssistProcessor implements IQuickAssistProcessor { private final HashMap<IMarker, ICompletionProposal[]> fResMap = new HashMap<IMarker, ICompletionProposal[]>(); public boolean canAssist(IQuickAssistInvocationContext invocationContext) { return true; } public boolean canFix(Annotation annotation) { if (!(annotation instanceof MarkerAnnotation)) { return false; } return CALDocumentProvider.canFix(annotation); } ICompletionProposal getResult(final CALBuilder.IQuickFix quickFix){ return new CP(quickFix); } private class CP implements ICompletionProposal, ICompletionProposalExtension2{ private final IQuickFix quickFix; CP(final CALBuilder.IQuickFix quickFix) { this.quickFix = quickFix; } public void apply(ITextViewer viewer, char trigger, int stateMask, int offset) { CompilerMessageLogger messageLogger = new MessageLogger(); final IDocument document = viewer.getDocument(); try { final int firstLine = document.getLineOfOffset(offset); final int column = CoreUtility.getColumn(firstLine, offset, document); final SourcePosition newSourcePosition = quickFix.applyFix(firstLine + 1, column + 1, messageLogger); CoreUtility.showErrors(ActionMessages.Error_messageDialog_title, ActionMessages.QuickAssist_failed, messageLogger); if (newSourcePosition != null){ final int cursorPosition = CoreUtility.toOffset(newSourcePosition, viewer.getDocument()); CALEditor.AdaptedSourceViewer asv = (AdaptedSourceViewer) viewer; asv.getEditor().selectAndReveal(cursorPosition, 0); } } catch (BadLocationException e) { } } public void selected(ITextViewer viewer, boolean smartToggle) { } public void unselected(ITextViewer viewer) { } public boolean validate(IDocument document, int offset, DocumentEvent event) { return true; } // This will not be called because the newer interface is used. public void apply(IDocument document) { } public String getAdditionalProposalInfo() { return null; } public IContextInformation getContextInformation() { return null; } public String getDisplayString() { return quickFix.getDescription(); } public Image getImage() { return null; } public Point getSelection(IDocument document) { return null; } }; public ICompletionProposal[] computeQuickAssistProposals(IQuickAssistInvocationContext invocationContext) { CALEditor.AdaptedSourceViewer sv = (CALEditor.AdaptedSourceViewer) invocationContext.getSourceViewer(); IAnnotationModel amodel = invocationContext.getSourceViewer().getAnnotationModel(); IDocument doc = invocationContext.getSourceViewer().getDocument(); int offset = invocationContext.getOffset(); ArrayList<ICompletionProposal> list = new ArrayList<ICompletionProposal>(); // look through the annotation for this line that have quick fixes. for (Iterator<Annotation> it = UnsafeCast.unsafeCast(amodel.getAnnotationIterator()); it.hasNext(); ) { Annotation key = it.next(); if (!(key instanceof MarkerAnnotation)) { continue; } MarkerAnnotation annotation = (MarkerAnnotation)key; IMarker marker = annotation.getMarker(); // The possible solutions are cached. ICompletionProposal[] mapping = fResMap.get(marker); if (mapping == null){ CALBuilder.IQuickFix[] qfs = CALDocumentProvider.getQuickFixes(annotation, sv.getModuleName(), sv.getSourceManagerFactory(true)); mapping = new ICompletionProposal[qfs.length]; for(int i = 0; i < qfs.length; ++i){ mapping[i] = getResult(qfs[i]); } fResMap.put(marker, mapping); } if (mapping != null) { Position pos = amodel.getPosition(annotation); try { int line = doc.getLineOfOffset(pos.getOffset()); int start = pos.getOffset(); String delim = doc.getLineDelimiter(line); int delimLength = delim != null ? delim.length() : 0; int end = doc.getLineLength(line) + start - delimLength; if (offset >= start && offset <= end) { for (final ICompletionProposal completionProposal : mapping) { list.add(completionProposal); } } } catch (BadLocationException e) { } } } return list.toArray(new ICompletionProposal[list.size()]); } // error messages from trying to generate quick fixes. public String getErrorMessage() { return null; } } @Override public IQuickAssistAssistant getQuickAssistAssistant(ISourceViewer sourceViewer){ QuickAssistAssistant qaa = new QuickAssistAssistant(); qaa.setQuickAssistProcessor(new QuickAssistProcessor()); return qaa; } /** * 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>JavaOutlineInformationControl</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 * @since 2.1 */ 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 CALOutlineInformationControl(parent, shellStyle, treeStyle, commandId); } }; } }