--- /dev/null
+package org.eclipse.cdt.internal.ui.text.c.hover;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Iterator;
+
+import org.eclipse.cdt.ui.CUIPreferenceInitializer;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.internal.text.html.BrowserInformationControlInput;
+import org.eclipse.jface.internal.text.html.HTML2TextReader;
+import org.eclipse.jface.internal.text.html.HTMLPrinter;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.text.AbstractInformationControl;
+import org.eclipse.jface.text.IDelayedInputChangeProvider;
+import org.eclipse.jface.text.IInformationControlExtension2;
+import org.eclipse.jface.text.IInputChangedListener;
+import org.eclipse.jface.text.TextPresentation;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTError;
+import org.eclipse.swt.browser.Browser;
+import org.eclipse.swt.browser.LocationListener;
+import org.eclipse.swt.browser.OpenWindowListener;
+import org.eclipse.swt.browser.ProgressAdapter;
+import org.eclipse.swt.browser.ProgressEvent;
+import org.eclipse.swt.browser.WindowEvent;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.graphics.TextLayout;
+import org.eclipse.swt.graphics.TextStyle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Slider;
+
+/**
+ * Displays doxygen HTML information.
+ */
+public class CDocBrowserInformationControl extends AbstractInformationControl implements
+ IInformationControlExtension2, IDelayedInputChangeProvider {
+
+ /**
+ * Tells whether the SWT Browser widget and hence this information
+ * control is available.
+ *
+ * @param parent the parent component used for checking or <code>null</code> if none
+ * @return <code>true</code> if this control is available
+ */
+ public static boolean isAvailable(Composite parent) {
+ if (!fgAvailabilityChecked) {
+ try {
+ Browser browser= new Browser(parent, SWT.NONE);
+ browser.dispose();
+ fgIsAvailable= true;
+
+ Slider sliderV= new Slider(parent, SWT.VERTICAL);
+ Slider sliderH= new Slider(parent, SWT.HORIZONTAL);
+ int width= sliderV.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
+ int height= sliderH.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
+ fgScrollBarSize= new Point(width, height);
+ sliderV.dispose();
+ sliderH.dispose();
+ } catch (SWTError er) {
+ fgIsAvailable= false;
+ } finally {
+ fgAvailabilityChecked= true;
+ }
+ }
+
+ return fgIsAvailable;
+ }
+
+ /**
+ * Minimal size constraints.
+ * @since 3.2
+ */
+ private static final int MIN_WIDTH= 80;
+
+ private static final int MIN_HEIGHT= 50;
+
+
+ /**
+ * Availability checking cache.
+ */
+ private static boolean fgIsAvailable= false;
+
+ private static boolean fgAvailabilityChecked= false;
+
+ /**
+ * Cached scroll bar width and height
+ * @since 3.4
+ */
+ private static Point fgScrollBarSize;
+
+ /** The control's browser widget */
+ private Browser fBrowser;
+
+ /** Tells whether the browser has content */
+ private boolean fBrowserHasContent;
+
+ /** Text layout used to approximate size of content when rendered in browser */
+ private TextLayout fTextLayout;
+
+ /** Bold text style */
+ private TextStyle fBoldStyle;
+
+ private BrowserInformationControlInput fInput;
+
+ /**
+ * <code>true</code> iff the browser has completed loading of the last
+ * input set via {@link #setInformation(String)}.
+ * @since 3.4
+ */
+ private boolean fCompleted= false;
+
+ /**
+ * The listener to be notified when a delayed location changing event happened.
+ * @since 3.4
+ */
+ private IInputChangedListener fDelayedInputChangeListener;
+
+ /**
+ * The listeners to be notified when the input changed.
+ * @since 3.4
+ */
+ private ListenerList/*<IInputChangedListener>*/fInputChangeListeners= new ListenerList(ListenerList.IDENTITY);
+
+ /**
+ * The symbolic name of the font used for size computations, or <code>null</code> to use dialog font.
+ * @since 3.4
+ */
+ private final String fSymbolicFontName;
+
+
+ /**
+ * Creates a browser information control with the given shell as parent.
+ *
+ * @param parent the parent shell
+ * @param symbolicFontName the symbolic name of the font used for size computations
+ * @param resizable <code>true</code> if the control should be resizable
+ * @since 3.4
+ */
+ public CDocBrowserInformationControl(Shell parent, String symbolicFontName, boolean resizable) {
+ super(parent, resizable);
+ fSymbolicFontName= symbolicFontName;
+ create();
+ }
+
+ /**
+ * Creates a browser information control with the given shell as parent.
+ *
+ * @param parent the parent shell
+ * @param symbolicFontName the symbolic name of the font used for size computations
+ * @param statusFieldText the text to be used in the optional status field
+ * or <code>null</code> if the status field should be hidden
+ * @since 3.4
+ */
+ public CDocBrowserInformationControl(Shell parent, String symbolicFontName, String statusFieldText) {
+ super(parent, statusFieldText);
+ fSymbolicFontName= symbolicFontName;
+ create();
+ }
+
+ /**
+ * Creates a browser information control with the given shell as parent.
+ *
+ * @param parent the parent shell
+ * @param symbolicFontName the symbolic name of the font used for size computations
+ * @param toolBarManager the manager or <code>null</code> if toolbar is not desired
+ * @since 3.4
+ */
+ public CDocBrowserInformationControl(Shell parent, String symbolicFontName, ToolBarManager toolBarManager) {
+ super(parent, toolBarManager);
+ fSymbolicFontName= symbolicFontName;
+ create();
+ }
+
+ /*
+ * @see org.eclipse.jface.text.AbstractInformationControl#createContent(org.eclipse.swt.widgets.Composite)
+ */
+ protected void createContent(Composite parent) {
+ fBrowser= new Browser(parent, SWT.NONE);
+ fBrowser.setJavascriptEnabled(false);
+
+ Display display= getShell().getDisplay();
+ fBrowser.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
+ fBrowser.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+
+ fBrowser.addProgressListener(new ProgressAdapter() {
+ public void completed(ProgressEvent event) {
+ fCompleted= true;
+ }
+ });
+
+ fBrowser.addOpenWindowListener(new OpenWindowListener() {
+ public void open(WindowEvent event) {
+ event.required= true; // Cancel opening of new windows
+ }
+ });
+
+ // Replace browser's built-in context menu with none
+ fBrowser.setMenu(new Menu(getShell(), SWT.NONE));
+
+ createTextLayout();
+ }
+
+ /**
+ * {@inheritDoc}
+ * @deprecated use {@link #setInput(Object)}
+ */
+ public void setInformation(final String content) {
+ setInput(new BrowserInformationControlInput(null) {
+ public String getHtml() {
+ return content;
+ }
+
+ public String getInputName() {
+ return ""; //$NON-NLS-1$
+ }
+
+ public Object getInputElement() {
+ return content;
+ }
+ });
+ }
+
+ /**
+ * {@inheritDoc} This control can handle {@link String} and
+ * {@link BrowserInformationControlInput}.
+ */
+ public void setInput(Object input) {
+ Assert.isLegal(input == null || input instanceof String || input instanceof BrowserInformationControlInput);
+
+ if (input instanceof String) {
+ setInformation((String)input);
+ return;
+ }
+
+ fInput= (BrowserInformationControlInput)input;
+
+ String content= null;
+ if (fInput != null)
+ content= fInput.getHtml();
+
+ fBrowserHasContent= content != null && content.length() > 0;
+
+ if (!fBrowserHasContent)
+ content= "<html><body ></html>"; //$NON-NLS-1$
+
+ boolean RTL= (getShell().getStyle() & SWT.RIGHT_TO_LEFT) != 0;
+ boolean resizable= isResizable();
+
+ // The default "overflow:auto" would not result in a predictable width for the client area
+ // and the re-wrapping would cause visual noise
+ String[] styles= null;
+ if (RTL && resizable)
+ styles= new String[] { "direction:rtl;", "overflow:scroll;", "word-wrap:break-word;" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ else if (RTL && !resizable)
+ styles= new String[] { "direction:rtl;", "overflow:hidden;", "word-wrap:break-word;" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ else if (!resizable)
+ //XXX: In IE, "word-wrap: break-word;" causes bogus wrapping even in non-broken words :-(see e.g. Javadoc of String).
+ // Re-check whether we really still need this now that the Javadoc Hover header already sets this style.
+ styles= new String[] { "overflow:hidden;"/*, "word-wrap: break-word;"*/}; //$NON-NLS-1$
+ else
+ styles= new String[] { "overflow:scroll;" }; //$NON-NLS-1$
+
+ StringBuffer buffer= new StringBuffer(content);
+ HTMLPrinter.insertStyles(buffer, styles);
+ content= buffer.toString();
+
+ /*
+ * XXX: Should add some JavaScript here that shows something like
+ * "(continued...)" or "..." at the end of the visible area when the page overflowed
+ * with "overflow:hidden;".
+ */
+
+ fCompleted= false;
+ if (!content.contains(".html")) {
+ return;
+ }
+ String url = CUIPreferenceInitializer.doxygenDirectoryPath + "/" + content;
+ CElementLinks.isSet = 1;
+ fBrowser.setUrl(url);
+
+ Object[] listeners= fInputChangeListeners.getListeners();
+ for (int i= 0; i < listeners.length; i++)
+ ((IInputChangedListener)listeners[i]).inputChanged(fInput);
+ }
+
+ /*
+ * @see IInformationControl#setVisible(boolean)
+ */
+ public void setVisible(boolean visible) {
+ Shell shell= getShell();
+ if (shell.isVisible() == visible)
+ return;
+
+ if (!visible) {
+ super.setVisible(false);
+ setInput(null);
+ return;
+ }
+
+ /*
+ * The Browser widget flickers when made visible while it is not completely loaded.
+ * The fix is to delay the call to setVisible until either loading is completed
+ * (see ProgressListener in constructor), or a timeout has been reached.
+ */
+ final Display display= shell.getDisplay();
+
+ // Make sure the display wakes from sleep after timeout:
+ display.timerExec(100, new Runnable() {
+ public void run() {
+ fCompleted= true;
+ }
+ });
+
+ while (!fCompleted) {
+ // Drive the event loop to process the events required to load the browser widget's contents:
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+
+ shell= getShell();
+ if (shell == null || shell.isDisposed())
+ return;
+
+ /*
+ * Avoids flickering when replacing hovers, especially on Vista in ON_CLICK mode.
+ * Causes flickering on GTK. Carbon does not care.
+ */
+ if ("win32".equals(SWT.getPlatform())) //$NON-NLS-1$
+ shell.moveAbove(null);
+
+ super.setVisible(true);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.AbstractInformationControl#setSize(int, int)
+ */
+ public void setSize(int width, int height) {
+ fBrowser.setRedraw(false); // avoid flickering
+ try {
+ //super.setSize(width, height);
+ super.setSize(750, 300); // TODO Modify
+ } finally {
+ fBrowser.setRedraw(true);
+ }
+ }
+
+ /**
+ * Creates and initializes the text layout used
+ * to compute the size hint.
+ *
+ * @since 3.2
+ */
+ private void createTextLayout() {
+ fTextLayout= new TextLayout(fBrowser.getDisplay());
+
+ // Initialize fonts
+ String symbolicFontName= fSymbolicFontName == null ? JFaceResources.DIALOG_FONT : fSymbolicFontName;
+ Font font= JFaceResources.getFont(symbolicFontName);
+ fTextLayout.setFont(font);
+ fTextLayout.setWidth(-1);
+ font= JFaceResources.getFontRegistry().getBold(symbolicFontName);
+ fBoldStyle= new TextStyle(font, null, null);
+
+ // Compute and set tab width
+ fTextLayout.setText(" "); //$NON-NLS-1$
+ int tabWidth= fTextLayout.getBounds().width;
+ fTextLayout.setTabs(new int[] { tabWidth });
+ fTextLayout.setText(""); //$NON-NLS-1$
+ }
+
+ /*
+ * @see org.eclipse.jface.text.AbstractInformationControl#handleDispose()
+ * @since 3.6
+ */
+ protected void handleDispose() {
+ if (fTextLayout != null) {
+ fTextLayout.dispose();
+ fTextLayout= null;
+ }
+ fBrowser= null;
+
+ super.handleDispose();
+ }
+
+ /*
+ * @see IInformationControl#computeSizeHint()
+ */
+ public Point computeSizeHint() {
+ Point sizeConstraints= getSizeConstraints();
+ Rectangle trim= computeTrim();
+ int height= trim.height;
+
+ //FIXME: The HTML2TextReader does not render <p> like a browser.
+ // Instead of inserting an empty line, it just adds a single line break.
+ // Furthermore, the indentation of <dl><dd> elements is too small (e.g with a long @see line)
+ TextPresentation presentation= new TextPresentation();
+ HTML2TextReader reader= new HTML2TextReader(new StringReader(fInput.getHtml()), presentation);
+ String text;
+ try {
+ text= reader.getString();
+ } catch (IOException e) {
+ text= ""; //$NON-NLS-1$
+ }
+
+ fTextLayout.setText(text);
+ fTextLayout.setWidth(sizeConstraints == null ? SWT.DEFAULT : sizeConstraints.x - trim.width);
+ Iterator iter= presentation.getAllStyleRangeIterator();
+ while (iter.hasNext()) {
+ StyleRange sr= (StyleRange)iter.next();
+ if (sr.fontStyle == SWT.BOLD)
+ fTextLayout.setStyle(fBoldStyle, sr.start, sr.start + sr.length - 1);
+ }
+
+ Rectangle bounds= fTextLayout.getBounds(); // does not return minimum width, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=217446
+ int lineCount= fTextLayout.getLineCount();
+ int textWidth= 0;
+ for (int i= 0; i < lineCount; i++) {
+ Rectangle rect= fTextLayout.getLineBounds(i);
+ int lineWidth= rect.x + rect.width;
+ if (i == 0)
+ lineWidth+= fInput.getLeadingImageWidth();
+ textWidth= Math.max(textWidth, lineWidth);
+ }
+ bounds.width= textWidth;
+ fTextLayout.setText(""); //$NON-NLS-1$
+
+ int minWidth= bounds.width;
+ height= height + bounds.height;
+
+ // Add some air to accommodate for different browser renderings
+ minWidth+= 15;
+ height+= 15;
+
+
+ // Apply max size constraints
+ if (sizeConstraints != null) {
+ if (sizeConstraints.x != SWT.DEFAULT)
+ minWidth= Math.min(sizeConstraints.x, minWidth + trim.width);
+ if (sizeConstraints.y != SWT.DEFAULT)
+ height= Math.min(sizeConstraints.y, height);
+ }
+
+ // Ensure minimal size
+ int width= Math.max(MIN_WIDTH, minWidth);
+ height= Math.max(MIN_HEIGHT, height);
+
+ return new Point(width, height);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IInformationControlExtension3#computeTrim()
+ */
+ public Rectangle computeTrim() {
+ Rectangle trim= super.computeTrim();
+ if (isResizable()) {
+ boolean RTL= (getShell().getStyle() & SWT.RIGHT_TO_LEFT) != 0;
+ if (RTL) {
+ trim.x-= fgScrollBarSize.x;
+ }
+ trim.width+= fgScrollBarSize.x;
+ trim.height+= fgScrollBarSize.y;
+ }
+ return trim;
+ }
+
+ /**
+ * Adds the listener to the collection of listeners who will be
+ * notified when the current location has changed or is about to change.
+ *
+ * @param listener the location listener
+ * @since 3.4
+ */
+ public void addLocationListener(LocationListener listener) {
+ fBrowser.addLocationListener(listener);
+ }
+
+ /*
+ * @see IInformationControl#setForegroundColor(Color)
+ */
+ public void setForegroundColor(Color foreground) {
+ super.setForegroundColor(foreground);
+ fBrowser.setForeground(foreground);
+ }
+
+ /*
+ * @see IInformationControl#setBackgroundColor(Color)
+ */
+ public void setBackgroundColor(Color background) {
+ super.setBackgroundColor(background);
+ fBrowser.setBackground(background);
+ }
+
+ /*
+ * @see IInformationControlExtension#hasContents()
+ */
+ public boolean hasContents() {
+ return fBrowserHasContent;
+ }
+
+ /**
+ * Adds a listener for input changes to this input change provider.
+ * Has no effect if an identical listener is already registered.
+ *
+ * @param inputChangeListener the listener to add
+ * @since 3.4
+ */
+ public void addInputChangeListener(IInputChangedListener inputChangeListener) {
+ Assert.isNotNull(inputChangeListener);
+ fInputChangeListeners.add(inputChangeListener);
+ }
+
+ /**
+ * Removes the given input change listener from this input change provider.
+ * Has no effect if an identical listener is not registered.
+ *
+ * @param inputChangeListener the listener to remove
+ * @since 3.4
+ */
+ public void removeInputChangeListener(IInputChangedListener inputChangeListener) {
+ fInputChangeListeners.remove(inputChangeListener);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IDelayedInputChangeProvider#setDelayedInputChangeListener(org.eclipse.jface.text.IInputChangedListener)
+ * @since 3.4
+ */
+ public void setDelayedInputChangeListener(IInputChangedListener inputChangeListener) {
+ fDelayedInputChangeListener= inputChangeListener;
+ }
+
+ /**
+ * Tells whether a delayed input change listener is registered.
+ *
+ * @return <code>true</code> iff a delayed input change
+ * listener is currently registered
+ * @since 3.4
+ */
+ public boolean hasDelayedInputChangeListener() {
+ return fDelayedInputChangeListener != null;
+ }
+
+ /**
+ * Notifies listeners of a delayed input change.
+ *
+ * @param newInput the new input, or <code>null</code> to request cancellation
+ * @since 3.4
+ */
+ public void notifyDelayedInputChange(Object newInput) {
+ if (fDelayedInputChangeListener != null)
+ fDelayedInputChangeListener.inputChanged(newInput);
+ }
+
+ /*
+ * @see java.lang.Object#toString()
+ * @since 3.4
+ */
+ public String toString() {
+ String style= (getShell().getStyle() & SWT.RESIZE) == 0 ? "fixed" : "resizeable"; //$NON-NLS-1$ //$NON-NLS-2$
+ return super.toString() + " - style: " + style; //$NON-NLS-1$
+ }
+
+ /**
+ * @return the current browser input or <code>null</code>
+ */
+ public BrowserInformationControlInput getInput() {
+ return fInput;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IInformationControlExtension5#computeSizeConstraints(int, int)
+ */
+ public Point computeSizeConstraints(int widthInChars, int heightInChars) {
+ if (fSymbolicFontName == null)
+ return null;
+
+ GC gc= new GC(fBrowser);
+ Font font= fSymbolicFontName == null ? JFaceResources.getDialogFont() : JFaceResources.getFont(fSymbolicFontName);
+ gc.setFont(font);
+ int width= gc.getFontMetrics().getAverageCharWidth();
+ int height= gc.getFontMetrics().getHeight();
+ gc.dispose();
+
+ return new Point(widthInChars * width, heightInChars * height);
+ }
+}
*******************************************************************************/
package org.eclipse.cdt.internal.ui.text.c.hover;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Set;
+import java.util.StringTokenizer;
+import org.eclipse.cdt.core.dom.ast.DOMException;
+import org.eclipse.cdt.core.dom.ast.IASTName;
+import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
+import org.eclipse.cdt.core.dom.ast.IBinding;
+import org.eclipse.cdt.core.model.CModelException;
+import org.eclipse.cdt.core.model.ILanguage;
+import org.eclipse.cdt.core.model.ITranslationUnit;
+import org.eclipse.cdt.core.model.IWorkingCopy;
+import org.eclipse.cdt.core.parser.KeywordSetKey;
+import org.eclipse.cdt.core.parser.ParserFactory;
+import org.eclipse.cdt.core.parser.ParserLanguage;
+import org.eclipse.cdt.internal.core.model.ASTCache.ASTRunnable;
+import org.eclipse.cdt.internal.corext.util.Strings;
+import org.eclipse.cdt.internal.ui.CHelpProviderManager;
+import org.eclipse.cdt.internal.ui.editor.ASTProvider;
+import org.eclipse.cdt.internal.ui.editor.CEditorMessages;
+import org.eclipse.cdt.internal.ui.text.CWordFinder;
+import org.eclipse.cdt.internal.ui.text.HTMLPrinter;
+import org.eclipse.cdt.ui.CUIPlugin;
+import org.eclipse.cdt.ui.CUIPreferenceInitializer;
+import org.eclipse.cdt.ui.IFunctionSummary;
+import org.eclipse.cdt.ui.IFunctionSummary.IFunctionPrototypeSummary;
+import org.eclipse.cdt.ui.IRequiredInclude;
+import org.eclipse.cdt.ui.IWorkingCopyManager;
+import org.eclipse.cdt.ui.PreferenceConstants;
+import org.eclipse.cdt.ui.text.ICHelpInvocationContext;
+import org.eclipse.cdt.ui.text.IHoverHelpInvocationContext;
import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.internal.text.html.BrowserInformationControlInput;
+import org.eclipse.jface.internal.text.html.BrowserInput;
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DefaultInformationControl;
+import org.eclipse.jface.text.IInformationControl;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.IInformationControlExtension4;
+import org.eclipse.jface.text.IInputChangedListener;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.Region;
+import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.IWorkbenchSite;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.editors.text.EditorsUI;
-import org.eclipse.cdt.core.model.ITranslationUnit;
-import org.eclipse.cdt.ui.CUIPlugin;
-import org.eclipse.cdt.ui.IFunctionSummary;
-import org.eclipse.cdt.ui.IRequiredInclude;
-import org.eclipse.cdt.ui.IFunctionSummary.IFunctionPrototypeSummary;
-import org.eclipse.cdt.ui.text.ICHelpInvocationContext;
-import org.eclipse.cdt.ui.text.IHoverHelpInvocationContext;
+/**
+ * Provides doxygen hover.
+ */
+public class CDocHover extends AbstractCEditorTextHover {
-import org.eclipse.cdt.internal.ui.CHelpProviderManager;
-import org.eclipse.cdt.internal.ui.editor.CEditorMessages;
-import org.eclipse.cdt.internal.ui.text.CWordFinder;
-import org.eclipse.cdt.internal.ui.text.HTMLPrinter;
+ private static String doxygenDirectoryPath;
-public class CDocHover extends AbstractCEditorTextHover {
-
/**
* Constructor for DefaultCEditorTextHover
*/
public CDocHover() {
+ doxygenDirectoryPath = CUIPreferenceInitializer.doxygenDirectoryPath;
+ }
+
+ private static class SingletonRule implements ISchedulingRule {
+ public static final ISchedulingRule INSTANCE = new SingletonRule();
+ public boolean contains(ISchedulingRule rule) {
+ return rule == this;
+ }
+ public boolean isConflicting(ISchedulingRule rule) {
+ return rule == this;
+ }
+ }
+
+ /**
+ * Computes the source location for a given identifier.
+ */
+ private static class ComputeSourceRunnable implements ASTRunnable {
+
+ private final IRegion fTextRegion;
+ private String fSource;
+
+ /**
+ * If method is in source file, this is used.
+ */
+ private final int methodInSource = 1001;
+ /**
+ * If method is in header file, this is used.
+ */
+ private final int methodInHeader = 1002;
+
+ public ComputeSourceRunnable(ITranslationUnit tUnit, IRegion textRegion) {
+ fTextRegion = textRegion;
+ fSource = null;
+ }
+
+ /**
+ * Tokenizer
+ *
+ * @param string string to tokenize
+ * @return array of tokenized string
+ */
+ private String[] tokenizeString(String string) {
+ StringTokenizer tokenizer = new StringTokenizer(string);
+ String tokenOfString[] = new String[tokenizer.countTokens()];
+ int index = 0;
+ while (tokenizer.hasMoreTokens()) {
+ tokenOfString[index] = tokenizer.nextToken();
+ index++;
+ }
+ return tokenOfString;
+ }
+ /**
+ * Tokenizer with delimiter
+ *
+ * @param string string to tokenize
+ * @param delimiter delimiter
+ * @return array of tokenized string
+ */
+ private String[] tokenizeStringWithDelimiter(String string, String delimiter) {
+ StringTokenizer tokenizer = new StringTokenizer(string, delimiter);
+ String tokenOfString[] = new String[tokenizer.countTokens()];
+ int index = 0;
+ while (tokenizer.hasMoreTokens()) {
+ tokenOfString[index] = tokenizer.nextToken();
+ index++;
+ }
+ return tokenOfString;
+ }
+ /**
+ * Check HTML file existence.
+ *
+ * @param htmlFileName HTML file name
+ * @return true if it exist, false if it doesn't exist
+ * @throws IOException if an I/O error has occurred
+ */
+ private boolean checkHTMLFileExistence(String htmlFileName) throws IOException {
+ File file = new File(doxygenDirectoryPath);
+ File fileList[] = file.listFiles();
+ for (int i = 0; i < fileList.length; i++) {
+ if (htmlFileName.equals(fileList[i].getName())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ /**
+ * Find location of member variable of class.
+ *
+ * @param htmlLine 1 line of HTML file
+ * @param memberName method name
+ * @return location of member variable of class n HTML file
+ */
+ private String searchMemberVariableLocationOfCalss(String htmlLine, String memberName) {
+ String tempString = null;
+ String htmlFile = null;
+ if (htmlLine.contains("class=\"memlist\"") == true) {
+ tempString = htmlLine.trim();
+ tempString = htmlLine.substring( htmlLine.indexOf(">", htmlLine.indexOf("href")) + 1, htmlLine.length());
+ if (tempString.indexOf(memberName) == 0) {
+ htmlFile = htmlLine.substring(htmlLine.indexOf("href") + 6, htmlLine.indexOf("\"", htmlLine.indexOf("href") + 6));
+ return htmlFile;
+ }
+ }
+ return null;
+ }
+ /**
+ * Change string with enumeration brace.
+ *
+ * @param str string with enumeration brace
+ * @return changed string
+ */
+ private String changeStringWithEnumerationBrace(String str) {
+ StringBuilder sb = new StringBuilder(str);
+ int index = 0;
+ int index2 = 0;
+ while (index != 4) {
+ index = sb.indexOf("enum {", index) + 5;
+ index2 = sb.indexOf("}", index);
+ if ((index2 != -1) && (index != -1)) {
+ sb.delete(index2, index2 + 1);
+ sb.delete(index, index + 1);
+ }
+ }
+ return sb.toString();
+ }
+ /**
+ * Change string with void.
+ *
+ * @param str string with void
+ * @return changed string
+ */
+ private String changeStringWithVoid(String str) {
+ StringBuilder sb = new StringBuilder(str);
+ int index = 0;
+ int index2 = 0;
+ while (index != -1) {
+ index = sb.indexOf("(void)", index);
+ index2 = index + 5;
+ if ((index2 != -1) && (index != -1)) {
+ sb.delete(index2, index2 + 1);
+ sb.delete(index, index + 1);
+ }
+ }
+ return sb.toString();
+ }
+ /**
+ * Change string with template.
+ *
+ * @param str string with template
+ * @return changed string
+ */
+ private String changeStringWithTemplate(String str) {
+ StringBuilder sb = new StringBuilder(str);
+ String type = str.substring(str.indexOf("<") + 1, str.indexOf(">"));
+ if (!type.contains(",")) {
+ str = str.replaceAll(type, "");
+ return str;
+ } else if (type.contains(",")) {
+ sb.replace(sb.indexOf("<") + 1, sb.indexOf(",", sb.indexOf("<")), "");
+ sb.replace(sb.indexOf(",", sb.indexOf("<")) + 1, sb.indexOf(">"), "");
+ return sb.toString();
+ }
+ return null;
+ }
+ /**
+ * Change string with asterisk.
+ *
+ * @param str string with asterisk
+ * @return changed string
+ */
+ private String changeStringWithAsterisk(String str) {
+ StringBuilder sb = new StringBuilder(str);
+ int index = 0;
+ while (index != -1) {
+ index = sb.indexOf("*", index);
+ if (index != -1) {
+ sb.delete(index, index + 1);
+ sb.replace(index - 1, index, "* ");
+ }
+ }
+
+ return sb.toString();
+ }
+ /**
+ * Find location of method of class in HTML file.
+ *
+ * @param htmlLine 1 line of HTML file
+ * @param binding IBinding
+ * @param isWhere methodInSource if method is in source file, methodInHeader if method is in header file
+ * @param isMethodOfClass true if method of class, false if method of interface
+ * @return location of method of class in HTML file
+ * @throws DOMException
+ */
+ private String searchMethodLocationOfClass(String htmlLine, IBinding binding, int isWhere, boolean isMethodOfClass) throws DOMException {
+ String htmlFile = null;
+
+ String str[] = tokenizeString(binding.toString());
+ if (str.length == 2) {
+ if (str[1].equals("CPPFIELD")) {
+ if ( (htmlFile = searchMemberVariableLocationOfCalss(htmlLine, str[0])) != null) {
+ return htmlFile;
+ }
+ }
+ }
+
+ String bindingString = binding.toString();
+ if (bindingString.contains("long int")) {
+ bindingString = bindingString.replaceAll("long int", "long");
+ }
+ if (bindingString.contains("short int")) {
+ bindingString = bindingString.replaceAll("short int", "short");
+ }
+ if (bindingString.contains("enum {")) {
+ bindingString = changeStringWithEnumerationBrace(bindingString);
+ }
+ if (bindingString.contains("enum ")) {
+ bindingString = bindingString.replaceAll("enum ", "");
+ }
+ /* handling defined type start */
+ if (bindingString.contains("wchar_t")) {
+ bindingString = bindingString.replaceAll("wchar_t", "mchar");
+ }
+ if (bindingString.contains("Buffer<double>")) {
+ bindingString = bindingString.replaceAll("Buffer<double>", "DoubleBuffer");
+ }
+ if (bindingString.contains("Buffer<float>")) {
+ bindingString = bindingString.replaceAll("Buffer<float>", "FloatBuffer");
+ }
+ if (bindingString.contains("Buffer<int>")) {
+ bindingString = bindingString.replaceAll("Buffer<int>", "IntBuffer");
+ }
+ if (bindingString.contains("Buffer<long>")) {
+ bindingString = bindingString.replaceAll("Buffer<long>", "LongBuffer");
+ }
+ if (bindingString.contains("Buffer<long long>")) {
+ bindingString = bindingString.replaceAll("Buffer<long long>", "LongLongBuffer");
+ }
+ if (bindingString.contains("Buffer<mchar>")) {
+ bindingString = bindingString.replaceAll("Buffer<mchar>", "McharBuffer");
+ }
+ if (bindingString.contains("Buffer<short>")) {
+ bindingString = bindingString.replaceAll("Buffer<short>", "ShortBuffer");
+ }
+ if (bindingString.contains("UUID_")) {
+ bindingString = bindingString.replaceAll("UUID_", "UUID");
+ }
+ /* handling defined type end */
+ if (bindingString.contains("(void)")) {
+ changeStringWithVoid(bindingString);
+ }
+ String ownerOfBinding = binding.getOwner().toString();
+ String namespace = ownerOfBinding.substring(0, ownerOfBinding.lastIndexOf(":") + 1);
+ String methodName = tokenizeStringWithDelimiter(bindingString, "(),")[0];
+ if (!methodName.equals("Compare")) {
+ if (!bindingString.contains("Osp::Base::Object")) {
+ if (bindingString.contains(namespace)) {
+ bindingString = bindingString.replaceAll(namespace, "");
+ }
+ }
+ } else if (methodName.equals("Compare")) {
+ if (!bindingString.contains("Osp::Base::Object")) {
+ if (bindingString.contains(namespace)) {
+ bindingString = bindingString.replaceAll(namespace, "");
+ }
+ }
+ }
+ if (ownerOfBinding.equals("Osp::Base::Utility::StringUtil CPPCLASSTYPE")) {
+ bindingString = bindingString.replaceAll("Osp::Base::", "");
+ }
+ if (ownerOfBinding.equals("Osp::Ui::Controls::Frame CPPCLASSTYPE")) {
+ if (!bindingString.contains("AddControl")) {
+ bindingString = bindingString.replaceAll("Osp::Ui::", "");
+ }
+ }
+ if (bindingString.contains("{")) {
+ bindingString = bindingString.substring(0, bindingString.indexOf("{") - 1);
+ bindingString = bindingString + " CPPMETHOD";
+ if (bindingString.contains("<")) {
+ bindingString = changeStringWithTemplate(bindingString);
+ }
+ }
+ bindingString = bindingString.replaceAll(" ", "");
+
+ String bindingStringMethod[] = tokenizeStringWithDelimiter(bindingString, "(),");
+ String htmlLineString = null;
+ int isWhereTemp = 0;
+ if (isWhere == methodInSource) {
+ isWhereTemp = 2;
+ } else if (isWhere == methodInHeader) {
+ isWhereTemp = 1;
+ }
+ boolean isSame = true;
+
+ if (htmlLine.contains("class=\"memlist\"") == true) {
+ htmlLineString = htmlLine.trim();
+ htmlLineString = htmlLine.substring( htmlLine.indexOf(">", htmlLine.indexOf("href")) + 1, htmlLine.length());
+ if (htmlLineString.contains("const ")) {
+ htmlLineString = htmlLineString.replaceAll("const ", "const");
+ }
+ if (htmlLineString.contains(" &")) {
+ htmlLineString = htmlLineString.replaceAll(" &", "& ");
+ }
+ if (htmlLineString.contains("*")) {
+ htmlLineString = changeStringWithAsterisk(htmlLineString);
+ }
+ if (htmlLineString.contains("< ")) {
+ htmlLineString = htmlLineString.replaceAll("< ", "<");
+ }
+ if (htmlLineString.contains(" >")) {
+ htmlLineString = htmlLineString.replaceAll(" >", ">");
+ }
+ if (htmlLineString.contains("<")) {
+ htmlLineString = changeStringWithTemplate(htmlLineString);
+ }
+ if (htmlLineString.contains("(") && htmlLineString.contains(")")) { /* if member of class is method*/
+ String tokenOfMethod1 = htmlLineString.substring(0, htmlLineString.indexOf("("));
+ String tokenOfMethod2 = htmlLineString.substring(htmlLineString.indexOf("("), htmlLineString.indexOf(")") + 1);
+ String tokenOfMethod3 = htmlLineString.substring(htmlLineString.indexOf(")") + 1, htmlLineString.length());
+ /* handling defined type start */
+ if (tokenOfMethod2.contains("AppId")) {
+ tokenOfMethod2 = tokenOfMethod2.replaceAll("AppId", "Osp::Base::String");
+ }
+ if (tokenOfMethod2.contains("AppSecret")) {
+ tokenOfMethod2 = tokenOfMethod2.replaceAll("AppSecret", "Osp::Base::String");
+ }
+ if (tokenOfMethod2.contains("BuddyCategoryId")) {
+ tokenOfMethod2 = tokenOfMethod2.replaceAll("BuddyCategoryId", "Osp::Base::String");
+ }
+ if (tokenOfMethod2.contains("ChatMsgId")) {
+ tokenOfMethod2 = tokenOfMethod2.replaceAll("ChatMsgId", "int");
+ }
+ if (tokenOfMethod2.contains("KeyData")) {
+ tokenOfMethod2 = tokenOfMethod2.replaceAll("KeyData", "char");
+ }
+ if (tokenOfMethod2.contains("LandmarkId")) {
+ tokenOfMethod2 = tokenOfMethod2.replaceAll("LandmarkId", "long");
+ }
+ if (tokenOfMethod2.contains("LoginId")) {
+ tokenOfMethod2 = tokenOfMethod2.replaceAll("LoginId", "Osp::Base::String");
+ }
+ if (tokenOfMethod2.contains("NetAccountId")) {
+ tokenOfMethod2 = tokenOfMethod2.replaceAll("NetAccountId", "int");
+ }
+ if (tokenOfMethod2.contains("RecordId")) {
+ tokenOfMethod2 = tokenOfMethod2.replaceAll("RecordId", "long long");
+ }
+ if (tokenOfMethod2.contains("RecurrenceId")) {
+ tokenOfMethod2 = tokenOfMethod2.replaceAll("RecurrenceId", "Osp::Base::DateTime");
+ }
+ if (tokenOfMethod2.contains("RequestId")) {
+ tokenOfMethod2 = tokenOfMethod2.replaceAll("RequestId", "long");
+ }
+ if (tokenOfMethod2.contains("result")) {
+ tokenOfMethod2 = tokenOfMethod2.replaceAll("result", "unsigned long");
+ }
+ if (tokenOfMethod2.contains("UserId")) {
+ tokenOfMethod2 = tokenOfMethod2.replaceAll("UserId", "Osp::Base::String");
+ }
+ if (tokenOfMethod2.contains("xmlChar")) {
+ tokenOfMethod2 = tokenOfMethod2.replaceAll("xmlChar", "unsigned char");
+ }
+ if (tokenOfMethod2.contains("byte")) {
+ tokenOfMethod2 = tokenOfMethod2.replaceAll("byte", "unsigned char");
+ }
+ if (tokenOfMethod2.contains("unsigned ")) {
+ tokenOfMethod2 = tokenOfMethod2.replaceAll("unsigned ", "unsigned");
+ }
+ if (tokenOfMethod2.contains("long long")) {
+ tokenOfMethod2 = tokenOfMethod2.replaceAll("long long", "longlong");
+ }
+ /* handling defined type end */
+ htmlLineString = tokenOfMethod1 + tokenOfMethod2 + tokenOfMethod3;
+ }
+ if (isMethodOfClass == false) {
+ htmlLineString = htmlLineString.replaceAll("=0", "");
+ }
+ if (htmlLineString.indexOf(bindingStringMethod[0]) == 0) { /* method name in HTML file and method[0] variable are same */
+ if (bindingStringMethod.length != isWhereTemp) { /* method has parameter variable */
+ if ( (bindingStringMethod.length == isWhereTemp + 1) && (bindingStringMethod[1].equals("void")) && (htmlLineString.contains("(void")) ) { /* method has void parameter variable */
+ htmlFile = htmlLine.substring(htmlLine.indexOf("href") + 6, htmlLine.indexOf("\"", htmlLine.indexOf("href") + 6));
+ return htmlFile;
+ }
+ htmlLineString = htmlLineString.substring(htmlLineString.indexOf("(") + 1, htmlLineString.indexOf(")"));
+ String htmlLineStringMethod[] = tokenizeStringWithDelimiter(htmlLineString, ", ");
+ if ( ( (bindingStringMethod.length - isWhereTemp) * 2 ) == htmlLineStringMethod.length ) { /* the number of parameter variable of method in HTML file and the number of parameter variable of method are same */
+ if (binding.toString().contains("enum {")) {
+ htmlFile = htmlLine.substring(htmlLine.indexOf("href") + 6, htmlLine.indexOf("\"", htmlLine.indexOf("href") + 6));
+ return htmlFile;
+ }
+ if (binding.getOwner().toString().contains("CPP_CLASS_INSTANCE")) {
+ htmlFile = htmlLine.substring(htmlLine.indexOf("href") + 6, htmlLine.indexOf("\"", htmlLine.indexOf("href") + 6));
+ return htmlFile;
+ }
+ for (int i = 0; i < bindingStringMethod.length - 1; i++) {
+ if ( !bindingStringMethod[i + 1].equals(htmlLineStringMethod[i * 2]) ) {
+ isSame = false;
+ break;
+ }
+ if (isSame == true) {
+ htmlFile = htmlLine.substring(htmlLine.indexOf("href") + 6, htmlLine.indexOf("\"", htmlLine.indexOf("href") + 6));
+ return htmlFile;
+ }
+ }
+ }
+ } else if (bindingStringMethod.length == isWhereTemp) { /* method doesn't have parameter variable */
+ htmlFile = htmlLine.substring(htmlLine.indexOf("href") + 6, htmlLine.indexOf("\"", htmlLine.indexOf("href") + 6));
+ return htmlFile;
+ }
+ }
+ }
+
+ return null;
+ }
+ /**
+ * Find location of method of class in HTML file, if mouseover event occurs in header file.
+ *
+ * @param htmlLine 1 line of HTML file
+ * @param binding IBinding
+ * @return location of method of class in HTML file
+ * @throws DOMException, IOExceptio
+ */
+ private String searchMethod(String htmlLine, IBinding binding) throws DOMException, IOException {
+ String stringOfOwner = binding.getOwner().toString();
+ String htmlFileName = null;
+ boolean isExist = false;
+
+ if (htmlLine.contains("class=\"el\"") == true) {
+ if (htmlLine.contains(">" + stringOfOwner + "<")) {
+ int beginIndex = htmlLine.indexOf("\">", htmlLine.indexOf(">" + stringOfOwner + "<") + stringOfOwner.length() + 4);
+ int endIndex = htmlLine.indexOf("<", beginIndex);
+ String namespace = htmlLine.substring(beginIndex + 2, endIndex);
+ if (checkIsInterface(stringOfOwner) == false) {
+ htmlFileName = "class" + namespace.replaceAll("::", "_1_1") +"_1_1" + stringOfOwner + "-members.html";
+
+ isExist = checkHTMLFileExistence(htmlFileName); /* check HTML file existence */
+
+ if (isExist == true) {
+ BufferedReader reader = new BufferedReader(new FileReader(doxygenDirectoryPath + "/" + htmlFileName));
+ String tempString = null;
+ String htmlFileName2 = null;
+ while ( (tempString = reader.readLine()) != null) {
+ if ( (htmlFileName2 = searchMethodLocationOfClass(tempString, binding, methodInHeader, true)) != null) {
+ break;
+ }
+ }
+ reader.close();
+ htmlFileName = htmlFileName2;
+ }
+
+ return htmlFileName;
+ } else if (checkIsInterface(stringOfOwner) == true) {
+ htmlFileName = "interface" + namespace.replaceAll("::", "_1_1") +"_1_1" + stringOfOwner + "-members.html";
+
+ isExist = checkHTMLFileExistence(htmlFileName); /* check HTML file existence */
+
+ if (isExist == true) {
+ BufferedReader reader = new BufferedReader(new FileReader(doxygenDirectoryPath + "/" + htmlFileName));
+ String tempString = null;
+ String htmlFileName2 = null;
+ while ( (tempString = reader.readLine()) != null) {
+ if ( (htmlFileName2 = searchMethodLocationOfClass(tempString, binding, methodInHeader, false)) != null) {
+ break;
+ }
+ }
+ reader.close();
+ htmlFileName = htmlFileName2;
+ }
+
+ return htmlFileName;
+ }
+ }
+ }
+
+ return null;
+ }
+ /**
+ * Check is owner a interface or a class.
+ *
+ * @param owner string of owner
+ * @return true if interface, false if class
+ */
+ private boolean checkIsInterface(String owner) {
+ boolean isInterface = false;
+ if (owner.substring(0, 1).equals("I")) {
+ if (Character.isUpperCase(owner.charAt(1)) == true) {
+ isInterface = true;
+ } else if (Character.isUpperCase(owner.charAt(1)) == false) {
+ isInterface = false;
+ }
+ } else if (!owner.substring(0, 1).equals("I")) {
+ isInterface = false;
+ }
+
+ return isInterface;
+ }
+ /**
+ * Check is owner a interface or a class.
+ *
+ * @param owner string array of owner
+ * @return true if interface, false if class
+ */
+ private boolean checkIsInterface(String[] owner) {
+ boolean isInterface = false;
+ if (owner[owner.length - 1].substring(0, 1).equals("I")) {
+ if (Character.isUpperCase(owner[owner.length - 1].charAt(1)) == true) {
+ isInterface = true;
+ } else if (Character.isUpperCase(owner[owner.length - 1].charAt(1)) == false) {
+ isInterface = false;
+ }
+ } else if (!owner[owner.length - 1].substring(0, 1).equals("I")) {
+ isInterface = false;
+ }
+
+ return isInterface;
+ }
+ /**
+ * Find location of enumerator in HTML file.
+ *
+ * @param htmlFileName name of HTML file
+ * @param binding IBinding
+ * @param isWhere methodInSource if enumerator is in source file, methodInHeader if enumerator is in header file
+ * @return location of enumerator in HTML file
+ * @throws IOException
+ */
+ private String searchEnumerator(String htmlFileName, IBinding binding, int isWhere) throws IOException {
+ String enumeratorName = null;
+ if (isWhere == methodInSource) {
+ enumeratorName = tokenizeString(binding.toString())[0];
+ } else if (isWhere == methodInHeader) {
+ enumeratorName = binding.getName();
+ }
+
+ boolean isExist = false;
+
+ isExist = checkHTMLFileExistence(htmlFileName); /* check HTML file existence */
+
+ if (isExist == true) {
+ BufferedReader reader = new BufferedReader(new FileReader(doxygenDirectoryPath + "/" + htmlFileName));
+ String htmlLine = null;
+ String htmlFileName2 = null;
+ while ( (htmlLine = reader.readLine()) != null) {
+ if (htmlLine.contains(enumeratorName)) {
+ htmlLine = reader.readLine();
+ htmlFileName2 = htmlLine.substring(htmlLine.indexOf("href") + 6, htmlLine.indexOf("\"", htmlLine.indexOf("href") + 6));
+ break;
+ }
+ }
+ reader.close();
+ htmlFileName = htmlFileName2;
+ } else if (isExist == false) {
+ return null;
+ }
+
+ return htmlFileName;
+ }
+ /**
+ * Find location of enumeration in HTML file.
+ *
+ * @param htmlFileName name of HTML file
+ * @param binding IBinding
+ * @return location of enumeration in HTML file
+ * @throws IOException
+ */
+ private String searchEnumeration(String htmlFileName, IBinding binding) throws IOException {
+ String enumerationName = null;
+ enumerationName = binding.getName();
+
+ boolean isExist = false;
+
+ isExist = checkHTMLFileExistence(htmlFileName); /* check HTML file existence */
+
+ if (isExist == true) {
+ BufferedReader reader = new BufferedReader(new FileReader(doxygenDirectoryPath + "/" + htmlFileName));
+ String htmlLine = null;
+ String htmlFileName2 = null;
+ while ( (htmlLine = reader.readLine()) != null) {
+ if (htmlLine.contains(enumerationName)) {
+ htmlLine = reader.readLine();
+ htmlFileName2 = htmlLine.substring(htmlLine.indexOf("href") + 6, htmlLine.indexOf("\"", htmlLine.indexOf("href") + 6));
+ break;
+ }
+ }
+ reader.close();
+ htmlFileName = htmlFileName2;
+ } else if (isExist == false) {
+ return null;
+ }
+
+ return htmlFileName;
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.core.model.ASTCache.ASTRunnable#runOnAST(org.eclipse.cdt.core.dom.ast.IASTTranslationUnit)
+ */
+ public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) {
+ if (ast != null) {
+ try {
+ IASTName name= ast.getNodeSelector(null).findEnclosingName(fTextRegion.getOffset(), fTextRegion.getLength());
+ if (name != null) {
+ IBinding binding= name.resolveBinding();
+
+ if (binding != null) {
+ String htmlFileName = null;
+ boolean isExist = false;
+
+ if (binding.getOwner() == null) {
+ String stringOfBinding[] = tokenizeString(binding.toString());
+ if (stringOfBinding.length == 2) { /* Case 1-1. using root namespace in source file */
+ if (stringOfBinding[0].equals("Osp") && stringOfBinding[1].equals("CPPNAMESPACE")) {
+ htmlFileName = "namespaceOsp.html";
+ isExist = checkHTMLFileExistence(htmlFileName);
+ }
+ } else if (stringOfBinding.length == 1) { /* Case 1-2. declaration root namespace in header or source file */
+ if (stringOfBinding[0].equals("Osp")) {
+ htmlFileName = "namespaceOsp.html";
+ isExist = checkHTMLFileExistence(htmlFileName);
+ }
+ }
+ } else if (binding.getOwner() != null) {
+ String stringOfOwner[] = tokenizeString(binding.getOwner().toString());
+ if (stringOfOwner.length == 3) {
+ if (stringOfOwner[2].equals("CPPENUMERATION")) {
+ String stringOfBinding[] = tokenizeString(binding.toString());
+ if (stringOfBinding[1].equals("CPPENUMERATOR")) { /* Case 2-0. enumerator in source file */
+ char firstChar = stringOfBinding[0].charAt(0);
+ if (firstChar == 'A') {
+ htmlFileName = "namespacemembers_eval.html";
+ } else {
+ for (int i = 66; i < 91; i++) {
+ if ((int)firstChar == i) {
+ htmlFileName = "namespacemembers_eval_0x" + Integer.toHexString(i + 32) + ".html";
+ }
+ }
+ }
+
+ String htmlFileName2 = null;
+ if ( (htmlFileName2 = searchEnumerator(htmlFileName, binding, methodInSource)) != null) {
+ isExist = true;
+ htmlFileName = htmlFileName2;
+ }
+ }
+ }
+ } else if (stringOfOwner.length == 2) {
+ if (stringOfOwner[1].equals("CPPNAMESPACE")) {
+ String stringOfBinding[] = tokenizeString(binding.toString());
+ if (stringOfBinding[1].equals("CPPNAMESPACE")) { /* Case 2-1-1. using namespace in source file */
+ htmlFileName = "namespace" + tokenizeString(binding.toString())[0].replaceAll("::", "_1_1") + ".html";
+ isExist = checkHTMLFileExistence(htmlFileName);
+ } else if (stringOfBinding[1].equals("CPPCLASSTYPE")) { /* Case 2-1-2. class in source file */
+ htmlFileName = "class" + stringOfBinding[0].replaceAll("::", "_1_1") + ".html";
+ isExist = checkHTMLFileExistence(htmlFileName); /* check HTML file existence */
+ } else if (stringOfBinding[1].equals("CPP_CLASS_TEMPLATE")) { /* Case 2-1-3. class template in source file */
+ htmlFileName = "class" + stringOfBinding[0].replaceAll("::", "_1_1") + ".html";
+ isExist = checkHTMLFileExistence(htmlFileName); /* check HTML file existence */
+ } /*else if (stringOfBinding[1].equals("CPPENUMERATOR")) { Case 2-1-4. enumerator in source file
+ char firstChar = stringOfBinding[0].charAt(0);
+ if (firstChar == 'A') {
+ htmlFileName = "namespacemembers_eval.html";
+ } else {
+ for (int i = 66; i < 91; i++) {
+ if ((int)firstChar == i) {
+ htmlFileName = "namespacemembers_eval_0x" + Integer.toHexString(i + 32) + ".html";
+ }
+ }
+ }
+
+ String htmlFileName2 = null;
+ if ( (htmlFileName2 = searchEnumerator(htmlFileName, binding, methodInSource)) != null) {
+ isExist = true;
+ htmlFileName = htmlFileName2;
+ }
+ }*/ else if (stringOfBinding.length == 3 && stringOfBinding[2].equals("CPPENUMERATION")) { /* Case 2-1-5. enumeration used as parameter variable in header file */
+ char firstChar = stringOfBinding[1].charAt(0);
+ if (firstChar == 'A') {
+ htmlFileName = "namespacemembers_enum.html";
+ } else {
+ for (int i = 66; i < 91; i++) {
+ if ((int)firstChar == i) {
+ htmlFileName = "namespacemembers_enum_0x" + Integer.toHexString(i + 32) + ".html";
+ }
+ }
+ }
+
+ String htmlFileName2 = null;
+ if ( (htmlFileName2 = searchEnumeration(htmlFileName, binding)) != null) {
+ isExist = true;
+ htmlFileName = htmlFileName2;
+ }
+ } else if (binding.toString().contains("CPPTYPEDEF") && binding.toString().contains("enum")) { /* Case 2-1-6. typedef enumeration used as parameter variable in header file */
+ char firstChar = stringOfBinding[0].charAt(0);
+ if (firstChar == 'A') {
+ htmlFileName = "namespacemembers_enum.html";
+ } else {
+ for (int i = 66; i < 91; i++) {
+ if ((int)firstChar == i) {
+ htmlFileName = "namespacemembers_enum_0x" + Integer.toHexString(i + 32) + ".html";
+ }
+ }
+ }
+
+ String htmlFileName2 = null;
+ if ( (htmlFileName2 = searchEnumeration(htmlFileName, binding)) != null) {
+ isExist = true;
+ htmlFileName = htmlFileName2;
+ }
+ }
+ } else if (stringOfOwner[1].equals("CPPCLASSTYPE")) { /* Case 2-2. constructor or member method in source file */
+ String tokenOfStringOfOwner[] = tokenizeStringWithDelimiter(stringOfOwner[0], "::");
+ if ( checkIsInterface(tokenOfStringOfOwner) == false ) { /* Case 2-2-1. constructor or member method of class in source file */
+ htmlFileName = "class" + stringOfOwner[0].replaceAll("::", "_1_1") + "-members.html";
+
+ isExist = checkHTMLFileExistence(htmlFileName); /* check HTML file existence */
+
+ if (isExist == true) {
+ BufferedReader reader = new BufferedReader(new FileReader(doxygenDirectoryPath + "/" + htmlFileName));
+ String tempString = null;
+ String htmlFileName2 = null;
+ while ( (tempString = reader.readLine()) != null) {
+ if ( (htmlFileName2 = searchMethodLocationOfClass(tempString, binding, methodInSource, true)) != null) {
+ break;
+ }
+ }
+ reader.close();
+ htmlFileName = htmlFileName2;
+ }
+ } else if ( checkIsInterface(tokenOfStringOfOwner) == true ) { /* Case 2-2-2. constructor or member method of interface in source file */
+ htmlFileName = "interface" + stringOfOwner[0].replaceAll("::", "_1_1") + "-members.html";
+
+ isExist = checkHTMLFileExistence(htmlFileName); /* check HTML file existence */
+
+ if (isExist == true) {
+ BufferedReader reader = new BufferedReader(new FileReader(doxygenDirectoryPath + "/" + htmlFileName));
+ String tempString = null;
+ String htmlFileName2 = null;
+ while ( (tempString = reader.readLine()) != null) {
+ if ( (htmlFileName2 = searchMethodLocationOfClass(tempString, binding, methodInSource, false)) != null) {
+ break;
+ }
+ }
+ reader.close();
+ htmlFileName = htmlFileName2;
+ }
+ }
+ } else if (stringOfOwner[1].equals("CPP_CLASS_INSTANCE")) { /* Case 2-3. member method with <type> in source file */
+ String tokenOfStringOfOwner[] = tokenizeStringWithDelimiter(stringOfOwner[0], "::");
+ if ( checkIsInterface(tokenOfStringOfOwner) == false ) { /* Case 2-3-1. member method with <type> of class in source file */
+ String owner = stringOfOwner[0].substring(0, stringOfOwner[0].indexOf("<"));
+ htmlFileName = "class" + owner.replaceAll("::", "_1_1") + "-members.html";
+
+ isExist = checkHTMLFileExistence(htmlFileName); /* check HTML file existence */
+
+ if (isExist == true) {
+ BufferedReader reader = new BufferedReader(new FileReader(doxygenDirectoryPath + "/" + htmlFileName));
+ String tempString = null;
+ String htmlFileName2 = null;
+ while ( (tempString = reader.readLine()) != null) {
+ if ( (htmlFileName2 = searchMethodLocationOfClass(tempString, binding, methodInSource, true)) != null) {
+ break;
+ }
+ }
+ reader.close();
+ htmlFileName = htmlFileName2;
+ }
+ } else if ( checkIsInterface(tokenOfStringOfOwner) == true ) { /* Case 2-3-2. member method with <type> of interface in source file */
+ String owner = stringOfOwner[0].substring(0, stringOfOwner[0].indexOf("<"));
+ htmlFileName = "interface" + owner.replaceAll("::", "_1_1") + "-members.html";
+
+ isExist = checkHTMLFileExistence(htmlFileName); /* check HTML file existence */
+
+ if (isExist == true) {
+ BufferedReader reader = new BufferedReader(new FileReader(doxygenDirectoryPath + "/" + htmlFileName));
+ String tempString = null;
+ String htmlFileName2 = null;
+ while ( (tempString = reader.readLine()) != null) {
+ if ( (htmlFileName2 = searchMethodLocationOfClass(tempString, binding, methodInSource, false)) != null) {
+ break;
+ }
+ }
+ reader.close();
+ htmlFileName = htmlFileName2;
+ }
+ }
+
+ }
+ } else if (stringOfOwner.length == 1) {
+ String stringOfClass[] = tokenizeStringWithDelimiter(binding.getClass().toString(), ".");
+ if ( stringOfClass[stringOfClass.length - 1].equals("CPPNamespace") ) { /* Case 3-1-1. declaration namespace in header or source file */
+ htmlFileName = "namespace" + binding.toString().replaceAll("::", "_1_1") + ".html";
+ isExist = checkHTMLFileExistence(htmlFileName); /* check HTML file existence */
+ } else if ( stringOfClass[stringOfClass.length - 1].equals("CPPClassType") ) { /* Case 3-1-2. class in header file */
+ htmlFileName = "class" + binding.getOwner().toString().replaceAll("::", "_1_1") + "_1_1" + binding.toString() + ".html";
+ isExist = checkHTMLFileExistence(htmlFileName); /* check HTML file existence */
+ } /*else if ( stringOfClass[stringOfClass.length - 1].equals("CPPEnumerator") ) { Case 3-1-3. enumerator in header file
+ char firstChar = binding.getName().charAt(0);
+ if (firstChar == 'A') {
+ htmlFileName = "namespacemembers_eval.html";
+ } else {
+ for (int i = 66; i < 91; i++) {
+ if ((int)firstChar == i) {
+ htmlFileName = "namespacemembers_eval_0x" + Integer.toHexString(i + 32) + ".html";
+ }
+ }
+ }
+
+ String htmlFileName2 = null;
+ if ( (htmlFileName2 = searchEnumerator(htmlFileName, binding, methodInHeader)) != null) {
+ isExist = true;
+ htmlFileName = htmlFileName2;
+ }
+ }*/ else if ( stringOfClass[stringOfClass.length - 1].equals("CPPEnumeration") ) { /* Case 3-1-4. declared enumeration in header file */
+ char firstChar = binding.getName().charAt(0);
+ if (firstChar == 'A') {
+ htmlFileName = "namespacemembers_enum.html";
+ } else {
+ for (int i = 66; i < 91; i++) {
+ if ((int)firstChar == i) {
+ htmlFileName = "namespacemembers_enum_0x" + Integer.toHexString(i + 32) + ".html";
+ }
+ }
+ }
+
+ String htmlFileName2 = null;
+ if ( (htmlFileName2 = searchEnumeration(htmlFileName, binding)) != null) {
+ isExist = true;
+ htmlFileName = htmlFileName2;
+ }
+ } else if ( stringOfClass[stringOfClass.length - 1].equals("CPPTypedef") ) { /* Case 3-1-5. typedef declared enumeration in header file */
+ char firstChar = binding.getName().charAt(0);
+ if (firstChar == 'A') {
+ htmlFileName = "namespacemembers_enum.html";
+ } else {
+ for (int i = 66; i < 91; i++) {
+ if ((int)firstChar == i) {
+ htmlFileName = "namespacemembers_enum_0x" + Integer.toHexString(i + 32) + ".html";
+ }
+ }
+ }
+
+ String htmlFileName2 = null;
+ if ( (htmlFileName2 = searchEnumeration(htmlFileName, binding)) != null) {
+ isExist = true;
+ htmlFileName = htmlFileName2;
+ }
+ } else if ( stringOfClass[stringOfClass.length - 1].equals("CPPClassType") || stringOfClass[stringOfClass.length - 1].equals("CPPMethod") ) { /* Case 3-1-6. constructor or member method in header file */
+ htmlFileName = "classes.html";
+
+ isExist = checkHTMLFileExistence(htmlFileName); /* check HTML file existence */
+
+ if (isExist == true) {
+ BufferedReader reader = new BufferedReader(new FileReader(doxygenDirectoryPath + "/" + htmlFileName));
+ String tempString = null;
+ String htmlFileName2 = null;
+ while ( (tempString = reader.readLine()) != null) {
+ if ( (htmlFileName2 = searchMethod(tempString, binding)) != null) {
+ break;
+ }
+ }
+ reader.close();
+ htmlFileName = htmlFileName2;
+ }
+ }
+ } else if (stringOfOwner.length == 0) {
+ String stringOfClass[] = tokenizeStringWithDelimiter(binding.getClass().toString(), ".");
+ if ( stringOfClass[stringOfClass.length - 1].equals("CPPEnumerator") ) { /* Case 4-1. enumerator in header file */
+ char firstChar = binding.getName().charAt(0);
+ if (firstChar == 'A') {
+ htmlFileName = "namespacemembers_eval.html";
+ } else {
+ for (int i = 66; i < 91; i++) {
+ if ((int)firstChar == i) {
+ htmlFileName = "namespacemembers_eval_0x" + Integer.toHexString(i + 32) + ".html";
+ }
+ }
+ }
+
+ String htmlFileName2 = null;
+ if ( (htmlFileName2 = searchEnumerator(htmlFileName, binding, methodInHeader)) != null) {
+ isExist = true;
+ htmlFileName = htmlFileName2;
+ }
+ }
+ }
+ }
+
+ if (isExist == true) {
+ fSource = htmlFileName;
+ }
+ /* TODO Modify End */
+
+ if (fSource != null) {
+ return Status.OK_STATUS;
+ }
+ }
+ }
+ } catch (DOMException exc) {
+ return new Status(IStatus.ERROR, CUIPlugin.PLUGIN_ID, "Internal Error", exc); //$NON-NLS-1$
+ } catch (Exception e) {
+ //
+ }
+ }
+ return Status.CANCEL_STATUS;
+ }
+
+ /**
+ * @return the computed source or <code>null</code>, if no source could be computed
+ */
+ public String getSource() {
+ return fSource;
+ }
+
}
/* (non-Javadoc)
return null;
}
+
+ /*
+ * @see ITextHover#getHoverInfo(ITextViewer, IRegion)
+ */
+ @Override
+ public Object getHoverInfo2(ITextViewer textViewer, IRegion hoverRegion) {
+ IEditorPart editor = getEditor();
+ if (editor != null) {
+ IEditorInput input = editor.getEditorInput();
+ IWorkingCopyManager manager = CUIPlugin.getDefault().getWorkingCopyManager();
+ IWorkingCopy copy = manager.getWorkingCopy(input);
+ try {
+ if (copy == null || !copy.isConsistent()) {
+ return null;
+ }
+ } catch (CModelException exc) {
+ return null;
+ }
+
+ String expression;
+ try {
+ expression = textViewer.getDocument().get(hoverRegion.getOffset(), hoverRegion.getLength());
+ expression = expression.trim();
+ if (expression.length() == 0)
+ return null;
+
+ //Before trying a search lets make sure that the user is not hovering over a keyword
+ if (selectionIsKeyword(expression))
+ return null;
+
+ String source = null;
+
+ // Try with the indexer
+ source= searchInIndex(copy, hoverRegion);
+
+ if (source == null || source.trim().length() == 0)
+ return null;
+
+ if (!source.contains(".html")) {
+ return null;
+ }
+
+ // we are actually interested in the comments, too.
+ //source= removeLeadingComments(source);
+
+ String delim= System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+
+ String[] sourceLines = Strings.convertIntoLines(source);
+ String firstLine = sourceLines[0];
+ if (!Character.isWhitespace(firstLine.charAt(0)))
+ sourceLines[0] = ""; //$NON-NLS-1$
+ Strings.trimIndentation(sourceLines, getTabWidth(), getTabWidth());
+
+ if (!Character.isWhitespace(firstLine.charAt(0)))
+ sourceLines[0] = firstLine;
+
+ source = Strings.concatenate(sourceLines, delim);
+
+ return internalGetHoverInfo(source, hoverRegion);
+ } catch (BadLocationException e) {
+ }
+ }
+ return null;
+ }
+
+ private CDocBrowserInformationControlInput internalGetHoverInfo(String elements, IRegion hoverRegion) {
+ return getHoverInfo(elements, null, hoverRegion, null);
+ }
+
+ private static CDocBrowserInformationControlInput getHoverInfo(String elements, String editorInputElement, IRegion hoverRegion, CDocBrowserInformationControlInput previousInput) {
+ int leadingImageWidth = 20;
+ return new CDocBrowserInformationControlInput(previousInput, elements, elements, leadingImageWidth);
+ }
+
+ private static int getTabWidth() {
+ return 4;
+ }
+
+ private String searchInIndex(final ITranslationUnit tUnit, IRegion textRegion) {
+ final ComputeSourceRunnable computer= new ComputeSourceRunnable(tUnit, textRegion);
+ Job job= new Job(CHoverMessages.CSourceHover_jobTitle) {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ return ASTProvider.getASTProvider().runOnAST(tUnit, ASTProvider.WAIT_ACTIVE_ONLY, monitor, computer);
+ } catch (Throwable t) {
+ CUIPlugin.log(t);
+ }
+ return Status.CANCEL_STATUS;
+ }
+ };
+ // If the hover thread is interrupted this might have negative
+ // effects on the index - see http://bugs.eclipse.org/219834
+ // Therefore we schedule a job to decouple the parsing from this thread.
+ job.setPriority(Job.DECORATE);
+ job.setSystem(true);
+ job.setRule(SingletonRule.INSTANCE);
+ job.schedule();
+ try {
+ job.join();
+ } catch (InterruptedException exc) {
+ job.cancel();
+ return null;
+ }
+ return computer.getSource();
+ }
+
+
+ /**
+ * Test whether the given name is a known keyword.
+ *
+ * @param name
+ * @return <code>true</code> if the name is a known keyword or <code>false</code> if the
+ * name is not considered a keyword
+ */
+ private boolean selectionIsKeyword(String name) {
+ Set<String> keywords = ParserFactory.getKeywordSet(KeywordSetKey.KEYWORDS, ParserLanguage.CPP);
+ return keywords.contains(name);
+ }
/* (non-Javadoc)
* @see org.eclipse.jface.text.ITextHover#getHoverRegion(org.eclipse.jface.text.ITextViewer, int)
}
return null;
}
-
+
+ /*
+ * @see org.eclipse.jface.text.ITextHoverExtension2#getInformationPresenterControlCreator()
+ * @since 3.1
+ */
+ public IInformationControlCreator getInformationPresenterControlCreator() {
+ if (fPresenterControlCreator == null)
+ fPresenterControlCreator = new PresenterControlCreator(getSite());
+ return fPresenterControlCreator;
+ }
+
+ private IWorkbenchSite getSite() {
+ IEditorPart editor= getEditor();
+ if (editor != null)
+ return editor.getSite();
+
+ return null;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.ITextHoverExtension#getHoverControlCreator()
+ * @since 3.0
+ */
+ @Override
+ public IInformationControlCreator getHoverControlCreator() {
+ if (fHoverControlCreator == null)
+ fHoverControlCreator = new HoverControlCreator(getInformationPresenterControlCreator());
+ return fHoverControlCreator;
+ }
+
+ private static void addLinkListener(final CDocBrowserInformationControl control) {
+ control.addLocationListener(CElementLinks.createLocationListener(new CElementLinks.ILinkHandler() {
+ /* (non-Javadoc)
+ * @see org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks.ILinkHandler#handleInlineJavadocLink(org.eclipse.jdt.core.IJavaElement)
+ */
+ public void handleInlineCDocLink(String linkTarget) {
+ CDocBrowserInformationControlInput hoverInfo = getHoverInfo(linkTarget, null, null, (CDocBrowserInformationControlInput) control.getInput());
+ if (control.hasDelayedInputChangeListener()) {
+ control.notifyDelayedInputChange(hoverInfo);
+ } else {
+ control.setInput(hoverInfo);
+ }
+ }
+
+ public void handleTextSet() {
+ }
+ }));
+ }
+
+ /**
+ * Get element name of back or forward action.
+ *
+ * @param htmlName HTML name
+ * @return element name
+ */
+ private static String getBackForwardElementName(String htmlName) {
+ String elementName = "";
+ try {
+ if (htmlName.equals("namespaceOsp.html")) {
+ elementName = "Osp";
+ } else if (htmlName.equals("privlevel.html")) {
+ elementName = "Privilege Level";
+ } else if (htmlName.equals("privgroup.html")) {
+ elementName = "Privilege Group";
+ } else if (htmlName.contains("_1_1")) {
+ if (!htmlName.contains("#")) {
+ elementName = htmlName.substring(htmlName.lastIndexOf("_1_1") + 4, htmlName.lastIndexOf(".html"));
+ } else if (htmlName.contains("#_details")) {
+ elementName = "Detailed Description";
+ } else if (htmlName.contains("#")) {
+ String htmlFileName = htmlName.substring(0, htmlName.lastIndexOf("#"));
+
+ File file = new File(doxygenDirectoryPath);
+ File fileList[] = file.listFiles();
+ BufferedReader reader = null;
+ for (int i = 0; i < fileList.length; i++) {
+ if (htmlFileName.equals(fileList[i].getName())) {
+ reader = new BufferedReader(new FileReader(doxygenDirectoryPath + "/" + htmlFileName));
+ String tempString = null;
+ while ( (tempString = reader.readLine()) != null) {
+ if (tempString.contains(htmlName)) {
+ int startIndex = tempString.indexOf(">", tempString.indexOf(htmlName)) + 1;
+ int endIndex = tempString.indexOf("</a>", startIndex);
+ elementName = tempString.substring(startIndex, endIndex);
+ break;
+ }
+ }
+ }
+ }
+ reader.close();
+ }
+ } else if (!htmlName.contains("_1_1")) {
+ elementName = htmlName.substring(0, htmlName.indexOf(".html"));
+ if (!htmlName.contains("#")) {
+ File file = new File(doxygenDirectoryPath);
+ File fileList[] = file.listFiles();
+ BufferedReader reader = null;
+ for (int i = 0; i < fileList.length; i++) {
+ reader = new BufferedReader(new FileReader(doxygenDirectoryPath + "/" + htmlName));
+ String tempString = null;
+ while ( (tempString = reader.readLine()) != null) {
+ if (tempString.contains("h1")) {
+ int startIndex = tempString.indexOf("h1") + 3;
+ int endIndex = tempString.indexOf("<", startIndex);
+ elementName = tempString.substring(startIndex, endIndex);
+ break;
+ }
+ }
+ }
+ reader.close();
+ } else if (htmlName.contains("#")) {
+ String htmlFileName = htmlName.substring(0, htmlName.lastIndexOf("#"));
+ String htmlName2 = htmlName.substring(htmlName.indexOf("#"), htmlName.length());
+ boolean isSearchFail = false;
+
+ File file = new File(doxygenDirectoryPath);
+ File fileList[] = file.listFiles();
+ BufferedReader reader = null;
+ for (int i = 0; i < fileList.length; i++) {
+ if (htmlFileName.equals(fileList[i].getName())) {
+ reader = new BufferedReader(new FileReader(doxygenDirectoryPath + "/" + htmlFileName));
+ String tempString = null;
+ while ( (tempString = reader.readLine()) != null) {
+ if (tempString.contains(htmlName)) {
+ int startIndex = tempString.indexOf(">", tempString.indexOf(htmlName)) + 1;
+ int endIndex = tempString.indexOf("</a>", startIndex);
+ elementName = tempString.substring(startIndex, endIndex);
+ isSearchFail = true;
+ break;
+ }
+ if (isSearchFail == false) {
+ if (tempString.contains(htmlName2)) {
+ int startIndex = tempString.indexOf(">", tempString.indexOf(htmlName2)) + 1;
+ int endIndex = tempString.indexOf("</a>", startIndex);
+ elementName = tempString.substring(startIndex, endIndex);
+ break;
+ }
+ }
+ }
+ }
+ }
+ reader.close();
+
+ if (elementName.contains("<span>")) {
+ elementName = elementName.replaceAll("<span>", "");
+ }
+ if (elementName.contains("</span>")) {
+ elementName = elementName.replaceAll("</span>", "");
+ }
+ }
+ }
+ } catch (IOException e) {
+ //
+ } catch (Exception e) {
+ //
+ }
+ return elementName;
+ }
+
+ /**
+ * Action to go back to the previous input in the hover control.
+ *
+ * @since 3.4
+ */
+ private static final class BackAction extends Action {
+ private final CDocBrowserInformationControl fInfoControl;
+
+ public BackAction(CDocBrowserInformationControl infoControl) {
+ fInfoControl= infoControl;
+ setText(CHoverMessages.CDocHover_back);
+ ISharedImages images = PlatformUI.getWorkbench().getSharedImages();
+ setImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_BACK));
+ setDisabledImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_BACK_DISABLED));
+
+ update();
+ }
+
+ public void run() {
+ BrowserInformationControlInput previous = (BrowserInformationControlInput) fInfoControl.getInput().getPrevious();
+ if (previous != null) {
+ fInfoControl.setInput(previous);
+ }
+ }
+
+ public void update() {
+ BrowserInformationControlInput current = fInfoControl.getInput();
+
+ if (current != null && current.getPrevious() != null) {
+ BrowserInput previous = current.getPrevious();
+ setToolTipText("Back to " + getBackForwardElementName(previous.toString()));
+ setEnabled(true);
+ } else {
+ setToolTipText(CHoverMessages.CDocHover_back);
+ setEnabled(false);
+ }
+ }
+ }
+
+ /**
+ * Action to go forward to the next input in the hover control.
+ *
+ * @since 3.4
+ */
+ private static final class ForwardAction extends Action {
+ private final CDocBrowserInformationControl fInfoControl;
+
+ public ForwardAction(CDocBrowserInformationControl infoControl) {
+ fInfoControl = infoControl;
+ setText(CHoverMessages.CDocHover_forward);
+ ISharedImages images = PlatformUI.getWorkbench().getSharedImages();
+ setImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_FORWARD));
+ setDisabledImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_FORWARD_DISABLED));
+
+ update();
+ }
+
+ public void run() {
+ BrowserInformationControlInput next = (BrowserInformationControlInput) fInfoControl.getInput().getNext();
+ if (next != null) {
+ fInfoControl.setInput(next);
+ }
+ }
+
+ public void update() {
+ BrowserInformationControlInput current = fInfoControl.getInput();
+
+ if (current != null && current.getNext() != null) {
+ setToolTipText("Forward to " + getBackForwardElementName(current.getNext().toString()));
+ setEnabled(true);
+ } else {
+ setToolTipText(CHoverMessages.CDocHover_forward);
+ setEnabled(false);
+ }
+ }
+ }
+
+ /**
+ * Presenter control creator.
+ *
+ * @since 3.3
+ */
+ public static final class PresenterControlCreator extends AbstractReusableInformationControlCreator {
+
+ private IWorkbenchSite fSite;
+
+ /**
+ * Creates a new PresenterControlCreator.
+ *
+ * @param site the site or <code>null</code> if none
+ * @since 3.6
+ */
+ public PresenterControlCreator(IWorkbenchSite site) {
+ fSite = site;
+ }
+
+ /*
+ * @see org.eclipse.jdt.internal.ui.text.java.hover.AbstractReusableInformationControlCreator#doCreateInformationControl(org.eclipse.swt.widgets.Shell)
+ */
+ public IInformationControl doCreateInformationControl(Shell parent) {
+ if (CDocBrowserInformationControl.isAvailable(parent)) {
+ ToolBarManager tbm = new ToolBarManager(SWT.FLAT);
+ String font = PreferenceConstants.EDITOR_TEXT_FONT;
+ CDocBrowserInformationControl iControl = new CDocBrowserInformationControl(parent, font, tbm);
+
+ final BackAction backAction = new BackAction(iControl);
+ backAction.setEnabled(false);
+ tbm.add(backAction);
+ final ForwardAction forwardAction = new ForwardAction(iControl);
+ tbm.add(forwardAction);
+ forwardAction.setEnabled(false);
+
+ IInputChangedListener inputChangeListener= new IInputChangedListener() {
+ public void inputChanged(Object newInput) {
+ backAction.update();
+ forwardAction.update();
+ }
+ };
+ iControl.addInputChangeListener(inputChangeListener);
+
+ tbm.update(true);
+
+ addLinkListener(iControl);
+ return iControl;
+
+ } else {
+ return new DefaultInformationControl(parent, true);
+ }
+ }
+ }
+
+ /**
+ * Hover control creator.
+ *
+ * @since 3.3
+ */
+ public static final class HoverControlCreator extends AbstractReusableInformationControlCreator {
+ /**
+ * The information presenter control creator.
+ * @since 3.4
+ */
+ private final IInformationControlCreator fInformationPresenterControlCreator;
+ /**
+ * <code>true</code> to use the additional info affordance, <code>false</code> to use the hover affordance.
+ */
+ private final boolean fAdditionalInfoAffordance;
+
+ /**
+ * @param informationPresenterControlCreator control creator for enriched hover
+ * @since 3.4
+ */
+ public HoverControlCreator(IInformationControlCreator informationPresenterControlCreator) {
+ this(informationPresenterControlCreator, false);
+ }
+
+ /**
+ * @param informationPresenterControlCreator control creator for enriched hover
+ * @param additionalInfoAffordance <code>true</code> to use the additional info affordance, <code>false</code> to use the hover affordance
+ * @since 3.4
+ */
+ public HoverControlCreator(IInformationControlCreator informationPresenterControlCreator, boolean additionalInfoAffordance) {
+ fInformationPresenterControlCreator = informationPresenterControlCreator;
+ fAdditionalInfoAffordance = additionalInfoAffordance;
+ }
+
+ /*
+ * @see org.eclipse.jdt.internal.ui.text.java.hover.AbstractReusableInformationControlCreator#doCreateInformationControl(org.eclipse.swt.widgets.Shell)
+ */
+ public IInformationControl doCreateInformationControl(Shell parent) {
+ String tooltipAffordanceString = fAdditionalInfoAffordance ? "showTextHoverAffordance" : EditorsUI.getTooltipAffordanceString();
+ if (CDocBrowserInformationControl.isAvailable(parent)) {
+ String font= PreferenceConstants.EDITOR_TEXT_FONT;
+ CDocBrowserInformationControl iControl = new CDocBrowserInformationControl(parent, font, tooltipAffordanceString) {
+ /*
+ * @see org.eclipse.jface.text.IInformationControlExtension5#getInformationPresenterControlCreator()
+ */
+ public IInformationControlCreator getInformationPresenterControlCreator() {
+ return fInformationPresenterControlCreator;
+ }
+ };
+ addLinkListener(iControl);
+ return iControl;
+ } else {
+ return new DefaultInformationControl(parent, tooltipAffordanceString);
+ }
+ }
+
+ /*
+ * @see org.eclipse.jdt.internal.ui.text.java.hover.AbstractReusableInformationControlCreator#canReuse(org.eclipse.jface.text.IInformationControl)
+ */
+ public boolean canReuse(IInformationControl control) {
+ if (!super.canReuse(control))
+ return false;
+
+ if (control instanceof IInformationControlExtension4) {
+ String tooltipAffordanceString = fAdditionalInfoAffordance ? "showTextHoverAffordance" : EditorsUI.getTooltipAffordanceString();
+ ((IInformationControlExtension4)control).setStatusText(tooltipAffordanceString);
+ }
+
+ return true;
+ }
+ }
+
+ /**
+ * The hover control creator.
+ *
+ * @since 3.2
+ */
+ private IInformationControlCreator fHoverControlCreator;
+ /**
+ * The presentation control creator.
+ *
+ * @since 3.2
+ */
+ private IInformationControlCreator fPresenterControlCreator;
}
import java.util.ArrayList;
import java.util.List;
-import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.jface.text.BadLocationException;
-import org.eclipse.jface.text.IDocument;
-import org.eclipse.jface.text.ITypedRegion;
-import org.eclipse.jface.text.TextUtilities;
-import org.eclipse.jface.text.contentassist.ICompletionProposal;
-import org.eclipse.swt.graphics.Image;
-
+import org.eclipse.cdt.core.CCorePlugin;
+import org.eclipse.cdt.core.IPositionConverter;
+import org.eclipse.cdt.core.dom.IName;
import org.eclipse.cdt.core.dom.ast.ASTTypeUtil;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTCompletionContext;
import org.eclipse.cdt.core.dom.ast.IASTCompletionNode;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
+import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
+import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTFunctionStyleMacroParameter;
import org.eclipse.cdt.core.dom.ast.IASTIdExpression;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNamedTypeSpecifier;
+import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorFunctionStyleMacroDefinition;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroDefinition;
+import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.ICompositeType;
import org.eclipse.cdt.core.dom.ast.IEnumeration;
import org.eclipse.cdt.core.dom.ast.IField;
import org.eclipse.cdt.core.dom.ast.IFunction;
import org.eclipse.cdt.core.dom.ast.IFunctionType;
+import org.eclipse.cdt.core.dom.ast.IMacroBinding;
import org.eclipse.cdt.core.dom.ast.IParameter;
+import org.eclipse.cdt.core.dom.ast.IProblemBinding;
import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.ITypedef;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMember;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPNamespace;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPSpecialization;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateDefinition;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPUsingDeclaration;
+import org.eclipse.cdt.core.dom.ast.gnu.c.ICASTKnRFunctionDeclarator;
+import org.eclipse.cdt.core.index.IIndex;
+import org.eclipse.cdt.core.index.IIndexName;
+import org.eclipse.cdt.core.model.ITranslationUnit;
+import org.eclipse.cdt.core.model.IWorkingCopy;
import org.eclipse.cdt.core.parser.ast.ASTAccessVisibility;
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
import org.eclipse.cdt.core.parser.util.IContentAssistMatcher;
-import org.eclipse.cdt.ui.CUIPlugin;
-import org.eclipse.cdt.ui.text.ICPartitions;
-
import org.eclipse.cdt.internal.core.dom.parser.c.CBuiltinParameter;
import org.eclipse.cdt.internal.core.dom.parser.c.CBuiltinVariable;
import org.eclipse.cdt.internal.core.dom.parser.c.CImplicitFunction;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPImplicitTypedef;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.AccessContext;
import org.eclipse.cdt.internal.core.parser.util.ContentAssistMatcherFactory;
-
+import org.eclipse.cdt.internal.ui.text.CHeuristicScanner;
+import org.eclipse.cdt.internal.ui.util.EditorUtility;
import org.eclipse.cdt.internal.ui.viewsupport.CElementImageProvider;
+import org.eclipse.cdt.ui.CUIPlugin;
+import org.eclipse.cdt.ui.IWorkingCopyManager;
+import org.eclipse.cdt.ui.text.ICPartitions;
+import org.eclipse.core.filebuffers.FileBuffers;
+import org.eclipse.core.filebuffers.ITextFileBuffer;
+import org.eclipse.core.filebuffers.ITextFileBufferManager;
+import org.eclipse.core.filebuffers.LocationKind;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextUtilities;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
/**
* Searches the DOM (both the AST and the index) for completion proposals.
* @author Bryan Wilkinson
*/
public class DOMCompletionProposalComputer extends ParsingBasedProposalComputer {
+ private final IProgressMonitor fMonitor;
+ private ITranslationUnit fTU;
/**
* Default constructor is required (executable extension).
*/
public DOMCompletionProposalComputer() {
+ fMonitor = new NullProgressMonitor();
+ IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ IWorkingCopy copy = null;
+ if (window != null) {
+ IWorkbenchPage page = window.getActivePage();
+ if (page != null) {
+ IEditorPart editor = page.getActiveEditor();
+ if (editor != null) {
+ IEditorInput input = editor.getEditorInput();
+ IWorkingCopyManager manager = CUIPlugin.getDefault().getWorkingCopyManager();
+ copy = manager.getWorkingCopy(input);
+ fTU = copy;
+ }
+ }
+ }
}
-
+
@Override
protected List<ICompletionProposal> computeCompletionProposals(
CContentAssistInvocationContext context,
- IASTCompletionNode completionNode, String prefix) {
+ IASTCompletionNode completionNode, String prefix) throws CoreException {
List<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>();
-
+
+ int indices[] = new int[200];
+ for (int i = 0; i < indices.length; i++) {
+ indices[i] = -1;
+ }
+
if (inPreprocessorDirective(context)) {
if (!inPreprocessorKeyword(context)) {
// add only macros
addMacroProposals(context, prefix, proposals);
}
} else {
+ IASTTranslationUnit ast = completionNode.getTranslationUnit();
+ String source = null;
+
boolean handleMacros= false;
IASTName[] names = completionNode.getNames();
if (name.getTranslationUnit() == null)
// The node isn't properly hooked up, must have backtracked out of this node
continue;
-
+
IASTCompletionContext astContext = name.getCompletionContext();
if (astContext == null) {
continue;
// handle macros only if there is a prefix
handleMacros = prefix.length() > 0;
}
-
+
IBinding[] bindings = astContext.findBindings(name, !context.isContextInformationStyle());
-
+
if (bindings != null) {
+ int index = 0;
+ int index2 = 0;
AccessContext accessibilityContext = new AccessContext(name);
for (IBinding binding : bindings) {
- if (accessibilityContext.isAccessible(binding))
+ if (accessibilityContext.isAccessible(binding)) {
handleBinding(binding, context, prefix, astContext, proposals);
+
+ source = computeSourceForBinding(ast, binding);
+ if (source != null) {
+ if ( !source.contains("@class") && !source.contains("@interface") && source.contains("@internal") ) {
+ indices[index2++] = index;
+ }
+ index++;
+ }
+ }
}
}
}
if (handleMacros)
addMacroProposals(context, prefix, proposals);
}
-
+
+ for (int i = indices.length - 1; i > -1; i--) {
+ if (indices[i] != -1) {
+ proposals.remove(indices[i]);
+ }
+ }
+
return proposals;
}
/**
+ * Find a definition or declaration for the given binding and returns the source for it.
+ * Definitions are preferred over declarations. In case of multiple definitions or declarations,
+ * and the first name which yields source is taken.
+ *
+ * @param ast the AST of the translation unit
+ * @param binding the binding
+ * @return a source string or <code>null</code>, if no source could be computed
+ * @throws CoreException if the source file could not be loaded or if there was a
+ * problem with the index
+ */
+ private String computeSourceForBinding(IASTTranslationUnit ast, IBinding binding) throws CoreException {
+ IName[] names = findDefsOrDecls(ast, binding);
+
+ // in case the binding is a non-explicit specialization we need
+ // to consider the original binding (bug 281396)
+ if (names.length == 0 && binding instanceof ICPPSpecialization) {
+ binding= ((ICPPSpecialization) binding).getSpecializedBinding();
+ if (!(binding instanceof IProblemBinding)) {
+ names= findDefsOrDecls(ast, binding);
+ }
+ }
+ if (names.length > 0) {
+ for (IName name : names) {
+ String source= computeSourceForName(name, binding);
+ if (source != null) {
+ return source;
+ }
+ }
+ }
+ return null;
+ }
+ /**
+ * Search for definitions or declarations for the given binding.
+ * @param ast the AST of the translation unit
+ * @param binding the binding
+ * @return an array of definitions or declarations, never <code>null</code>
+ * @throws CoreException
+ */
+ private IName[] findDefsOrDecls(IASTTranslationUnit ast, IBinding binding) throws CoreException {
+ IName[] names= findDefinitions(ast, binding);
+ if (names.length == 0) {
+ names= findDeclarations(ast, binding);
+ }
+ return names;
+ }
+ /**
+ * Search for definitions for the given binding.
+ *
+ * @param ast the AST of the translation unit
+ * @param binding the binding
+ * @return an array of definitions, never <code>null</code>
+ * @throws CoreException
+ */
+ private IName[] findDefinitions(IASTTranslationUnit ast, IBinding binding) throws CoreException {
+ IName[] declNames= ast.getDefinitionsInAST(binding);
+ if (declNames.length == 0 && ast.getIndex() != null) {
+ // search definitions in index
+ declNames = ast.getIndex().findNames(binding, IIndex.FIND_DEFINITIONS | IIndex.SEARCH_ACROSS_LANGUAGE_BOUNDARIES);
+ }
+ return declNames;
+ }
+ /**
+ * Search for declarations for the given binding.
+ *
+ * @param ast the AST of the translation unit
+ * @param binding the binding
+ * @return an array of declarations, never <code>null</code>
+ * @throws CoreException
+ */
+ private IName[] findDeclarations(IASTTranslationUnit ast, IBinding binding) throws CoreException {
+ IName[] declNames= ast.getDeclarationsInAST(binding);
+ if (declNames.length == 0 && ast.getIndex() != null) {
+ // search declarations in index
+ declNames= ast.getIndex().findNames(binding, IIndex.FIND_DECLARATIONS | IIndex.SEARCH_ACROSS_LANGUAGE_BOUNDARIES);
+ }
+ return declNames;
+ }
+ /**
+ * Get the source for the given name from the underlying file.
+ *
+ * @param name the name to get the source for
+ * @param binding the binding of the name
+ * @return the source string or <code>null</code>, if the source could not be computed
+ * @throws CoreException if the file could not be loaded
+ */
+ private String computeSourceForName(IName name, IBinding binding) throws CoreException {
+ IASTFileLocation fileLocation= name.getFileLocation();
+ if (fileLocation == null) {
+ return null;
+ }
+ int nodeOffset= fileLocation.getNodeOffset();
+ int nodeLength= fileLocation.getNodeLength();
+
+ String fileName= fileLocation.getFileName();
+ //if (DEBUG) System.out.println("[CSourceHover] Computing source for " + name + " in " + fileName); //$NON-NLS-1$//$NON-NLS-2$
+ IPath location= Path.fromOSString(fileName);
+ LocationKind locationKind= LocationKind.LOCATION;
+ if (name instanceof IASTName && !name.isReference()) {
+ IASTName astName= (IASTName)name;
+ if (astName.getTranslationUnit().getFilePath().equals(fileName) && fTU.getResource() != null) {
+ // reuse editor buffer for names local to the translation unit
+ location= fTU.getResource().getFullPath();
+ locationKind= LocationKind.IFILE;
+ }
+ } else {
+ // try to resolve path to a resource for proper encoding (bug 221029)
+ IFile file= EditorUtility.getWorkspaceFileAtLocation(location, fTU);
+ if (file != null) {
+ location= file.getFullPath();
+ locationKind= LocationKind.IFILE;
+ if (name instanceof IIndexName) {
+ // need to adjust index offsets to current offsets
+ // in case file has been modified since last index time
+ IIndexName indexName= (IIndexName) name;
+ long timestamp= indexName.getFile().getTimestamp();
+ IPositionConverter converter= CCorePlugin.getPositionTrackerManager().findPositionConverter(file, timestamp);
+ if (converter != null) {
+ IRegion currentLocation= converter.historicToActual(new Region(nodeOffset, nodeLength));
+ nodeOffset= currentLocation.getOffset();
+ nodeLength= currentLocation.getLength();
+ }
+ }
+ }
+ }
+ ITextFileBufferManager mgr= FileBuffers.getTextFileBufferManager();
+ mgr.connect(location, locationKind, fMonitor);
+ ITextFileBuffer buffer= mgr.getTextFileBuffer(location, locationKind);
+ try {
+ IRegion nameRegion= new Region(nodeOffset, nodeLength);
+ final int nameOffset= nameRegion.getOffset();
+ final int sourceStart;
+ final int sourceEnd;
+ IDocument doc= buffer.getDocument();
+ if (nameOffset >= doc.getLength() || nodeLength <= 0) {
+ return null;
+ }
+ if (binding instanceof IMacroBinding) {
+ ITypedRegion partition= TextUtilities.getPartition(doc, ICPartitions.C_PARTITIONING, nameOffset, false);
+ if (ICPartitions.C_PREPROCESSOR.equals(partition.getType())) {
+ int directiveStart= partition.getOffset();
+ int commentStart= searchCommentBackward(doc, directiveStart, -1);
+ if (commentStart >= 0) {
+ sourceStart= commentStart;
+ } else {
+ sourceStart= directiveStart;
+ }
+ sourceEnd= directiveStart + partition.getLength();
+ } else {
+ return null;
+ }
+ } else {
+ // expand source range to include preceding comment, if any
+ boolean isKnR= isKnRSource(name);
+ sourceStart= computeSourceStart(doc, nameOffset, binding, isKnR);
+ if (sourceStart == CHeuristicScanner.NOT_FOUND) {
+ return null;
+ }
+ sourceEnd= computeSourceEnd(doc, nameOffset + nameRegion.getLength(), binding, name.isDefinition(), isKnR);
+ }
+ String source= buffer.getDocument().get(sourceStart, sourceEnd - sourceStart);
+ return source;
+
+ } catch (BadLocationException exc) {
+ // ignore - should not happen anyway
+ //if (DEBUG) exc.printStackTrace();
+ } finally {
+ mgr.disconnect(location, LocationKind.LOCATION, fMonitor);
+ }
+ return null;
+ }
+ /**
+ * Searches the start of the comment preceding the given source offset.
+ * Continuous line comments are considered as one comment until a block
+ * comment is reached or a non-comment partition.
+ *
+ * @param doc the document
+ * @param start the start of the backward search
+ * @param bound search boundary (exclusive)
+ * @return the comment start offset or <code>-1</code>, if no suitable comment was found
+ * @throws BadLocationException
+ */
+ private static int searchCommentBackward(IDocument doc, int start, int bound) throws BadLocationException {
+ int firstLine= doc.getLineOfOffset(start);
+ if (firstLine == 0) {
+ return 0;
+ }
+ ITypedRegion partition= TextUtilities.getPartition(doc, ICPartitions.C_PARTITIONING, start, true);
+ int currentOffset= Math.max(doc.getLineOffset(firstLine - 1), partition.getOffset() - 1);
+ int commentOffset= -1;
+ while (currentOffset > bound) {
+ partition= TextUtilities.getPartition(doc, ICPartitions.C_PARTITIONING, currentOffset, true);
+ currentOffset= partition.getOffset() - 1;
+ if (ICPartitions.C_MULTI_LINE_COMMENT.equals(partition.getType())
+ || ICPartitions.C_MULTI_LINE_DOC_COMMENT.equals(partition.getType())) {
+ final int partitionOffset= partition.getOffset();
+ final int startLine= doc.getLineOfOffset(partitionOffset);
+ final int lineOffset= doc.getLineOffset(startLine);
+ if (partitionOffset == lineOffset ||
+ doc.get(lineOffset, partitionOffset - lineOffset).trim().length() == 0) {
+ return lineOffset;
+ }
+ return commentOffset;
+ } else if (ICPartitions.C_SINGLE_LINE_COMMENT.equals(partition.getType())
+ || ICPartitions.C_SINGLE_LINE_DOC_COMMENT.equals(partition.getType())) {
+ final int partitionOffset= partition.getOffset();
+ final int startLine= doc.getLineOfOffset(partitionOffset);
+ final int lineOffset= doc.getLineOffset(startLine);
+ if (partitionOffset == lineOffset ||
+ doc.get(lineOffset, partitionOffset - lineOffset).trim().length() == 0) {
+ commentOffset= lineOffset;
+ continue;
+ }
+ return commentOffset;
+ } else if (IDocument.DEFAULT_CONTENT_TYPE.equals(partition.getType())) {
+ if (doc.get(partition.getOffset(), partition.getLength()).trim().length() == 0) {
+ continue;
+ }
+ if (commentOffset >= 0) {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ return commentOffset;
+ }
+ /**
+ * Determine if the name is part of a KnR function definition.
+ * @param name
+ * @return <code>true</code> if the name is part of a KnR function
+ */
+ private boolean isKnRSource(IName name) {
+ if (name instanceof IASTName) {
+ IASTNode node= (IASTNode)name;
+ while (node.getParent() != null) {
+ if (node instanceof ICASTKnRFunctionDeclarator) {
+ return node.getParent() instanceof IASTFunctionDefinition;
+ }
+ node= node.getParent();
+ }
+ }
+ return false;
+ }
+ private int computeSourceStart(IDocument doc, int nameOffset, IBinding binding, boolean isKnR) throws BadLocationException {
+ int sourceStart= nameOffset;
+ CHeuristicScanner scanner= new CHeuristicScanner(doc);
+ if (binding instanceof IParameter) {
+ if (isKnR) {
+ sourceStart= scanner.scanBackward(nameOffset, CHeuristicScanner.UNBOUND, new char[] { ')', ';' });
+ } else {
+ sourceStart= scanner.scanBackward(nameOffset, CHeuristicScanner.UNBOUND, new char[] { '>', '(', ',' });
+ if (sourceStart > 0 && doc.getChar(sourceStart) == '>') {
+ sourceStart= scanner.findOpeningPeer(sourceStart - 1, '<', '>');
+ if (sourceStart > 0) {
+ sourceStart= scanner.scanBackward(sourceStart, CHeuristicScanner.UNBOUND, new char[] { '(', ',' });
+ }
+ }
+ }
+ if (sourceStart == CHeuristicScanner.NOT_FOUND) {
+ return sourceStart;
+ }
+ sourceStart= scanner.findNonWhitespaceForward(sourceStart + 1, nameOffset);
+ if (sourceStart == CHeuristicScanner.NOT_FOUND) {
+ sourceStart = nameOffset;
+ }
+ } else if (binding instanceof ICPPTemplateParameter) {
+ sourceStart= scanner.scanBackward(nameOffset, CHeuristicScanner.UNBOUND, new char[] { '>', '<', ',' });
+ if (sourceStart > 0 && doc.getChar(sourceStart) == '>') {
+ sourceStart= scanner.findOpeningPeer(sourceStart - 1, '<', '>');
+ if (sourceStart > 0) {
+ sourceStart= scanner.scanBackward(sourceStart, CHeuristicScanner.UNBOUND, new char[] { '<', ',' });
+ }
+ }
+ if (sourceStart == CHeuristicScanner.NOT_FOUND) {
+ return sourceStart;
+ }
+ sourceStart= scanner.findNonWhitespaceForward(sourceStart + 1, nameOffset);
+ if (sourceStart == CHeuristicScanner.NOT_FOUND) {
+ sourceStart = nameOffset;
+ }
+ } else if (binding instanceof IEnumerator) {
+ sourceStart= scanner.scanBackward(nameOffset, CHeuristicScanner.UNBOUND, new char[] { '{', ',' });
+ if (sourceStart == CHeuristicScanner.NOT_FOUND) {
+ return sourceStart;
+ }
+ sourceStart= scanner.findNonWhitespaceForward(sourceStart + 1, nameOffset);
+ if (sourceStart == CHeuristicScanner.NOT_FOUND) {
+ sourceStart = nameOffset;
+ }
+ } else {
+ final boolean expectClosingBrace;
+ IType type= null;
+ if (binding instanceof ITypedef) {
+ type= ((ITypedef)binding).getType();
+ } else if (binding instanceof IVariable) {
+ type= ((IVariable)binding).getType();
+ }
+ expectClosingBrace= type instanceof ICompositeType || type instanceof IEnumeration;
+ final int nameLine= doc.getLineOfOffset(nameOffset);
+ sourceStart= nameOffset;
+ int commentBound;
+ if (isKnR) {
+ commentBound= scanner.scanBackward(sourceStart, CHeuristicScanner.UNBOUND, new char[] { ')', ';' });
+ } else {
+ commentBound= scanner.scanBackward(sourceStart, CHeuristicScanner.UNBOUND, new char[] { '{', '}', ';' });
+ }
+ while (expectClosingBrace && commentBound > 0 && doc.getChar(commentBound) == '}') {
+ int openingBrace= scanner.findOpeningPeer(commentBound - 1, '{', '}');
+ if (openingBrace != CHeuristicScanner.NOT_FOUND) {
+ sourceStart= openingBrace - 1;
+ }
+ if (isKnR) {
+ commentBound= scanner.scanBackward(sourceStart, CHeuristicScanner.UNBOUND, new char[] { ')', ';' });
+ } else {
+ commentBound= scanner.scanBackward(sourceStart, CHeuristicScanner.UNBOUND, new char[] { '{', '}', ';' });
+ }
+ }
+ if (commentBound == CHeuristicScanner.NOT_FOUND) {
+ commentBound= -1; // unbound
+ }
+ sourceStart= Math.min(sourceStart, doc.getLineOffset(nameLine));
+ int commentStart= searchCommentBackward(doc, sourceStart, commentBound);
+ if (commentStart >= 0) {
+ sourceStart= commentStart;
+ } else {
+ int nextNonWS= scanner.findNonWhitespaceForward(commentBound+1, sourceStart);
+ if (nextNonWS != CHeuristicScanner.NOT_FOUND) {
+ int nextNonWSLine= doc.getLineOfOffset(nextNonWS);
+ int lineOffset= doc.getLineOffset(nextNonWSLine);
+ if (doc.get(lineOffset, nextNonWS - lineOffset).trim().length() == 0) {
+ sourceStart= doc.getLineOffset(nextNonWSLine);
+ }
+ }
+ }
+ }
+ return sourceStart;
+ }
+ private int computeSourceEnd(IDocument doc, int start, IBinding binding, boolean isDefinition, boolean isKnR) throws BadLocationException {
+ int sourceEnd= start;
+ CHeuristicScanner scanner= new CHeuristicScanner(doc);
+ // expand forward to the end of the definition/declaration
+ boolean searchBrace= false;
+ boolean searchSemi= false;
+ boolean searchComma= false;
+ if (binding instanceof ICompositeType || binding instanceof IEnumeration) {
+ searchBrace= true;
+ } else if (binding instanceof ICPPTemplateDefinition) {
+ searchBrace= true;
+ } else if (binding instanceof IFunction && isDefinition) {
+ searchBrace= true;
+ } else if (binding instanceof IParameter) {
+ if (isKnR) {
+ searchSemi= true;
+ } else {
+ searchComma= true;
+ }
+ } else if (binding instanceof IEnumerator || binding instanceof ICPPTemplateParameter) {
+ searchComma= true;
+ } else if (binding instanceof IVariable || binding instanceof ITypedef) {
+ searchSemi= true;
+ } else if (!isDefinition) {
+ searchSemi= true;
+ }
+ if (searchBrace) {
+ int brace= scanner.scanForward(start, CHeuristicScanner.UNBOUND, '{');
+ if (brace != CHeuristicScanner.NOT_FOUND) {
+ sourceEnd= scanner.findClosingPeer(brace + 1, '{', '}');
+ if (sourceEnd == CHeuristicScanner.NOT_FOUND) {
+ sourceEnd= doc.getLength();
+ }
+ }
+ // expand region to include whole line
+ IRegion lineRegion= doc.getLineInformationOfOffset(sourceEnd);
+ sourceEnd= lineRegion.getOffset() + lineRegion.getLength();
+ } else if (searchSemi) {
+ int semi= scanner.scanForward(start, CHeuristicScanner.UNBOUND, ';');
+ if (semi != CHeuristicScanner.NOT_FOUND) {
+ sourceEnd= semi+1;
+ }
+ // expand region to include whole line
+ IRegion lineRegion= doc.getLineInformationOfOffset(sourceEnd);
+ sourceEnd= lineRegion.getOffset() + lineRegion.getLength();
+ } else if (searchComma) {
+ int bound;
+ if (binding instanceof IParameter) {
+ bound= scanner.findClosingPeer(start, '(', ')');
+ } else if (binding instanceof ICPPTemplateParameter) {
+ bound= scanner.findClosingPeer(start, '<', '>');
+ } else if (binding instanceof IEnumerator) {
+ bound= scanner.findClosingPeer(start, '{', '}');
+ } else {
+ bound = CHeuristicScanner.NOT_FOUND;
+ }
+ if (bound == CHeuristicScanner.NOT_FOUND) {
+ bound= Math.min(doc.getLength(), start + 100);
+ }
+ int comma= scanner.scanForward(start, bound, ',');
+ if (comma == CHeuristicScanner.NOT_FOUND) {
+ // last argument
+ sourceEnd= bound;
+ } else {
+ sourceEnd= comma;
+ // expand region to include whole line if rest is comment
+ IRegion lineRegion= doc.getLineInformationOfOffset(sourceEnd);
+ int lineEnd= lineRegion.getOffset() + lineRegion.getLength();
+ int nextNonWS= scanner.findNonWhitespaceForwardInAnyPartition(sourceEnd + 1, lineEnd);
+ if (nextNonWS != CHeuristicScanner.NOT_FOUND) {
+ String contentType= TextUtilities.getContentType(doc, ICPartitions.C_PARTITIONING, nextNonWS, false);
+ if (ICPartitions.C_MULTI_LINE_COMMENT.equals(contentType) || ICPartitions.C_SINGLE_LINE_COMMENT.equals(contentType)) {
+ sourceEnd= lineEnd;
+ }
+ }
+ }
+ }
+ return sourceEnd;
+ }
+
+ /**
* Test whether the invocation offset is inside or before the preprocessor directive keyword.
*
* @param context the invocation context