mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-06-07 01:36:01 +02:00
Fix for 80831: Hover help should find declarations from included files
and related bugs 80829, 80836, 158867
This commit is contained in:
parent
ad8800e447
commit
efe180944f
1 changed files with 497 additions and 39 deletions
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2002, 2006 QNX Software Systems and others.
|
||||
* Copyright (c) 2002, 2007 QNX Software Systems and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
|
@ -7,25 +7,26 @@
|
|||
*
|
||||
* Contributors:
|
||||
* QNX Software Systems - Initial API and implementation
|
||||
* Anton Leherbauer (Wind River Systems)
|
||||
*******************************************************************************/
|
||||
|
||||
package org.eclipse.cdt.internal.ui.text.c.hover;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.cdt.core.model.CModelException;
|
||||
import org.eclipse.cdt.core.model.ICElement;
|
||||
import org.eclipse.cdt.core.model.ISourceReference;
|
||||
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.ui.codemanipulation.StubUtility;
|
||||
import org.eclipse.cdt.internal.ui.text.CCodeReader;
|
||||
import org.eclipse.cdt.internal.ui.util.Strings;
|
||||
import org.eclipse.cdt.ui.CUIPlugin;
|
||||
import org.eclipse.cdt.ui.IWorkingCopyManager;
|
||||
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.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.NullProgressMonitor;
|
||||
import org.eclipse.core.runtime.Path;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.jface.text.BadLocationException;
|
||||
import org.eclipse.jface.text.Document;
|
||||
import org.eclipse.jface.text.IDocument;
|
||||
|
@ -34,17 +35,388 @@ import org.eclipse.jface.text.IInformationControlCreator;
|
|||
import org.eclipse.jface.text.IRegion;
|
||||
import org.eclipse.jface.text.ITextHoverExtension;
|
||||
import org.eclipse.jface.text.ITextViewer;
|
||||
import org.eclipse.jface.text.ITypedRegion;
|
||||
import org.eclipse.jface.text.Region;
|
||||
import org.eclipse.jface.text.TextUtilities;
|
||||
import org.eclipse.jface.text.information.IInformationProviderExtension2;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.widgets.Shell;
|
||||
import org.eclipse.ui.IEditorInput;
|
||||
import org.eclipse.ui.IEditorPart;
|
||||
|
||||
import org.eclipse.cdt.core.dom.IName;
|
||||
import org.eclipse.cdt.core.dom.ast.DOMException;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTFunctionStyleMacroParameter;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTName;
|
||||
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.IEnumerator;
|
||||
import org.eclipse.cdt.core.dom.ast.IFunction;
|
||||
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.ITypedef;
|
||||
import org.eclipse.cdt.core.dom.ast.IVariable;
|
||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateDefinition;
|
||||
import org.eclipse.cdt.core.index.IIndex;
|
||||
import org.eclipse.cdt.core.index.IIndexName;
|
||||
import org.eclipse.cdt.core.model.CModelException;
|
||||
import org.eclipse.cdt.core.model.ICElement;
|
||||
import org.eclipse.cdt.core.model.ISourceRange;
|
||||
import org.eclipse.cdt.core.model.ISourceReference;
|
||||
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.ui.CUIPlugin;
|
||||
import org.eclipse.cdt.ui.IWorkingCopyManager;
|
||||
import org.eclipse.cdt.ui.text.ICPartitions;
|
||||
|
||||
import org.eclipse.cdt.internal.core.model.ASTCache.ASTRunnable;
|
||||
import org.eclipse.cdt.internal.core.model.ext.ICElementHandle;
|
||||
|
||||
import org.eclipse.cdt.internal.ui.editor.ASTProvider;
|
||||
import org.eclipse.cdt.internal.ui.text.CCodeReader;
|
||||
import org.eclipse.cdt.internal.ui.text.CHeuristicScanner;
|
||||
import org.eclipse.cdt.internal.ui.util.Strings;
|
||||
import org.eclipse.cdt.internal.ui.viewsupport.CElementLabels;
|
||||
import org.eclipse.cdt.internal.ui.viewsupport.IndexUI;
|
||||
|
||||
/**
|
||||
* CSourceHover
|
||||
* A text hover presenting the source of the element under the cursor.
|
||||
*/
|
||||
public class CSourceHover extends AbstractCEditorTextHover implements ITextHoverExtension, IInformationProviderExtension2 {
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
/**
|
||||
* Computes the source location for a given identifier.
|
||||
*/
|
||||
private static class ComputeSourceRunnable implements ASTRunnable {
|
||||
|
||||
private final ITranslationUnit fTU;
|
||||
private final IRegion fTextRegion;
|
||||
private final IProgressMonitor fMonitor;
|
||||
private String fSource;
|
||||
|
||||
/**
|
||||
* @param tUnit
|
||||
* @param textRegion
|
||||
*/
|
||||
public ComputeSourceRunnable(ITranslationUnit tUnit, IRegion textRegion) {
|
||||
fTU= tUnit;
|
||||
fTextRegion= textRegion;
|
||||
fMonitor= new NullProgressMonitor();
|
||||
fSource= null;
|
||||
}
|
||||
|
||||
/*
|
||||
* @see org.eclipse.cdt.internal.core.model.ASTCache.ASTRunnable#runOnAST(org.eclipse.cdt.core.dom.ast.IASTTranslationUnit)
|
||||
*/
|
||||
public IStatus runOnAST(IASTTranslationUnit ast) {
|
||||
if (ast != null) {
|
||||
try {
|
||||
IASTName[] names;
|
||||
names = fTU.getLanguage().getSelectedNames(ast, fTextRegion.getOffset(), fTextRegion.getLength());
|
||||
if (names != null && names.length == 1) {
|
||||
IBinding binding= names[0].resolveBinding();
|
||||
if (binding != null) {
|
||||
if (binding instanceof IProblemBinding) {
|
||||
if (DEBUG) fSource= "Cannot resolve " + new String(names[0].toCharArray()); //$NON-NLS-1$
|
||||
} else if (binding instanceof IMacroBinding) {
|
||||
fSource= computeSourceForMacro(ast, names[0], binding);
|
||||
} else {
|
||||
fSource= computeSourceForBinding(ast, binding);
|
||||
}
|
||||
if (fSource != null) {
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (CoreException exc) {
|
||||
return exc.getStatus();
|
||||
} catch (DOMException exc) {
|
||||
return new Status(IStatus.ERROR, CUIPlugin.PLUGIN_ID, "Internal Error", exc); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
return Status.CANCEL_STATUS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the source for a macro. If the source location of the macro definition can be determined,
|
||||
* the source is taken from there, otherwise the source is constructed as a <code>#define</code> directive.
|
||||
*
|
||||
* @param ast the AST of the translation unit
|
||||
* @param name the macro occurrence in the AST
|
||||
* @param binding the binding of the macro name
|
||||
* @return the source or <code>null</code>
|
||||
* @throws CoreException
|
||||
*/
|
||||
private String computeSourceForMacro(IASTTranslationUnit ast, IASTName name, IBinding binding) throws CoreException {
|
||||
IASTPreprocessorMacroDefinition macroDef= null;
|
||||
final char[] macroName= name.toCharArray();
|
||||
|
||||
// search for macro definition, there should be a more efficient way
|
||||
IASTPreprocessorMacroDefinition[] macroDefs;
|
||||
final IASTPreprocessorMacroDefinition[] localMacroDefs= ast.getMacroDefinitions();
|
||||
for (macroDefs= localMacroDefs; macroDefs != null; macroDefs= (macroDefs == localMacroDefs) ? ast.getBuiltinMacroDefinitions() : null) {
|
||||
for (int i = 0; i < macroDefs.length; i++) {
|
||||
if (Arrays.equals(macroDefs[i].getName().toCharArray(), macroName)) {
|
||||
macroDef= macroDefs[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (macroDef != null) {
|
||||
String source= computeSourceForName(macroDef.getName(), binding);
|
||||
if (source != null) {
|
||||
return source;
|
||||
}
|
||||
IASTFunctionStyleMacroParameter[] parameters= {};
|
||||
if (macroDef instanceof IASTPreprocessorFunctionStyleMacroDefinition) {
|
||||
parameters= ((IASTPreprocessorFunctionStyleMacroDefinition)macroDef).getParameters();
|
||||
}
|
||||
StringBuffer buf= new StringBuffer(macroName.length + macroDef.getExpansion().length() + parameters.length*5 + 10);
|
||||
buf.append("#define ").append(macroName); //$NON-NLS-1$
|
||||
if (parameters.length > 0) {
|
||||
buf.append('(');
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
if (i > 0) {
|
||||
buf.append(", "); //$NON-NLS-1$
|
||||
}
|
||||
IASTFunctionStyleMacroParameter parameter = parameters[i];
|
||||
buf.append(parameter.getParameter());
|
||||
}
|
||||
buf.append(')');
|
||||
}
|
||||
String expansion= macroDef.getExpansion();
|
||||
if (expansion != null) {
|
||||
buf.append(' ').append(expansion);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws DOMException if there was an internal problem with the DOM
|
||||
*/
|
||||
private String computeSourceForBinding(IASTTranslationUnit ast, IBinding binding) throws CoreException, DOMException {
|
||||
IName[] names= findDefinitions(ast, binding);
|
||||
if (names.length == 0) {
|
||||
names= findDeclarations(ast, binding);
|
||||
}
|
||||
if (names.length > 0) {
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
String source= computeSourceForName(names[0], binding);
|
||||
if (source != null) {
|
||||
return source;
|
||||
}
|
||||
}
|
||||
// fallback: return signature only
|
||||
ICElementHandle cElement= null;
|
||||
if (names[0] instanceof IIndexName) {
|
||||
cElement= IndexUI.getCElementForName(fTU.getCProject(), ast.getIndex(), (IIndexName)names[0]);
|
||||
} else if (names[0] instanceof IASTName) {
|
||||
cElement= IndexUI.getCElementForName(fTU.getCProject(), ast.getIndex(), (IASTName)names[0]);
|
||||
}
|
||||
if (cElement != null) {
|
||||
return CElementLabels.getTextLabel(cElement, CElementLabels.DEFAULT_QUALIFIED);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
String fileName= fileLocation.getFileName();
|
||||
if (DEBUG) System.out.println("[CSourceHover] Computing source for " + new String(name.toCharArray()) + " in " + fileName); //$NON-NLS-1$//$NON-NLS-2$
|
||||
IPath location= Path.fromOSString(fileName);
|
||||
LocationKind locationKind= LocationKind.LOCATION;
|
||||
if (name instanceof IASTName) {
|
||||
IASTName astName= (IASTName)name;
|
||||
if (astName.getContainingFilename().equals(fileName)) {
|
||||
// reuse editor buffer for names local to the translation unit
|
||||
location= fTU.getPath();
|
||||
locationKind= LocationKind.IFILE;
|
||||
}
|
||||
}
|
||||
ITextFileBufferManager mgr= FileBuffers.getTextFileBufferManager();
|
||||
mgr.connect(location, locationKind, fMonitor);
|
||||
ITextFileBuffer buffer= mgr.getTextFileBuffer(location, locationKind);
|
||||
String source;
|
||||
try {
|
||||
IRegion nameRegion= new Region(fileLocation.getNodeOffset(), fileLocation.getNodeLength());
|
||||
final int nameOffset= nameRegion.getOffset();
|
||||
int sourceStart= nameOffset;
|
||||
int sourceEnd= nameOffset + nameRegion.getLength();
|
||||
IDocument doc= buffer.getDocument();
|
||||
// expand source range to include preceding comment, if any
|
||||
CHeuristicScanner scanner= new CHeuristicScanner(doc);
|
||||
int commentBound;
|
||||
if (binding instanceof IParameter) {
|
||||
sourceStart= scanner.scanBackward(nameOffset, CHeuristicScanner.UNBOUND, new char[] { '(', ',' });
|
||||
if (sourceStart == CHeuristicScanner.NOT_FOUND) {
|
||||
return null;
|
||||
}
|
||||
sourceStart= scanner.findNonWhitespaceForward(sourceStart + 1, nameOffset);
|
||||
} else if (binding instanceof IEnumerator) {
|
||||
sourceStart= scanner.scanBackward(nameOffset, CHeuristicScanner.UNBOUND, new char[] { '{', ',' });
|
||||
if (sourceStart == CHeuristicScanner.NOT_FOUND) {
|
||||
return null;
|
||||
}
|
||||
sourceStart= scanner.findNonWhitespaceForward(sourceStart + 1, nameOffset);
|
||||
} else {
|
||||
final int nameLine= doc.getLineOfOffset(nameOffset);
|
||||
sourceStart= doc.getLineOffset(nameLine);
|
||||
commentBound= scanner.scanBackward(sourceStart, CHeuristicScanner.UNBOUND, new char[] { '{', '}', ';' });
|
||||
if (commentBound == CHeuristicScanner.NOT_FOUND) {
|
||||
commentBound= -1;
|
||||
}
|
||||
int commentStart= searchCommentBackward(doc, sourceStart, commentBound);
|
||||
if (commentStart >= 0) {
|
||||
sourceStart= commentStart;
|
||||
}
|
||||
}
|
||||
// expand forward to the end of the definition/declaration
|
||||
if (binding instanceof IMacroBinding) {
|
||||
ITypedRegion partition= TextUtilities.getPartition(doc, ICPartitions.C_PARTITIONING, nameOffset, false);
|
||||
if (ICPartitions.C_PREPROCESSOR.equals(partition.getType())) {
|
||||
sourceStart= partition.getOffset();
|
||||
sourceEnd= sourceStart + partition.getLength();
|
||||
}
|
||||
} else {
|
||||
boolean searchBrace= false;
|
||||
boolean searchSemi= false;
|
||||
boolean searchComma= false;
|
||||
if (!name.isDefinition()) {
|
||||
searchSemi= true;
|
||||
} else if (binding instanceof ICompositeType || binding instanceof IEnumeration || binding instanceof IFunction) {
|
||||
searchBrace= true;
|
||||
} else if (binding instanceof ICPPTemplateDefinition) {
|
||||
searchBrace= true;
|
||||
} else if (binding instanceof IParameter || binding instanceof IEnumerator) {
|
||||
searchComma= true;
|
||||
} else if (binding instanceof IVariable || binding instanceof ITypedef) {
|
||||
searchSemi= true;
|
||||
}
|
||||
if (searchBrace) {
|
||||
int brace= scanner.scanForward(nameOffset, 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(nameOffset, 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= Math.min(doc.getLength(), nameOffset + 100);
|
||||
int comma= scanner.scanForward(nameOffset, bound, ',');
|
||||
if (comma != CHeuristicScanner.NOT_FOUND) {
|
||||
sourceEnd= comma;
|
||||
}
|
||||
// expand region to include whole line if rest is white space or comment
|
||||
int nextNonWS= scanner.findNonWhitespaceForward(sourceEnd + 1, bound);
|
||||
if (doc.getLineOfOffset(nextNonWS) > doc.getLineOfOffset(sourceEnd)) {
|
||||
IRegion lineRegion= doc.getLineInformationOfOffset(sourceEnd);
|
||||
sourceEnd= lineRegion.getOffset() + lineRegion.getLength();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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().findDefinitions(binding);
|
||||
}
|
||||
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);
|
||||
}
|
||||
return declNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the computed source or <code>null</code>, if no source could be computed
|
||||
*/
|
||||
public String getSource() {
|
||||
return fSource;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -72,26 +444,22 @@ public class CSourceHover extends AbstractCEditorTextHover implements ITextHover
|
|||
if (expression.length() == 0)
|
||||
return null;
|
||||
|
||||
String source = null;
|
||||
String source= null;
|
||||
|
||||
ICElement curr = copy.getElement(expression);
|
||||
if (curr == null) {
|
||||
// Try with the indexer
|
||||
source = findMatches(expression, textViewer);
|
||||
source= searchInIndex(expression, hoverRegion);
|
||||
} else {
|
||||
source= ((ISourceReference) curr).getSource();
|
||||
source= getSourceForCElement(textViewer.getDocument(), curr);
|
||||
}
|
||||
if (source == null || source.trim().length() == 0)
|
||||
return null;
|
||||
|
||||
source= removeLeadingComments(source);
|
||||
String delim= null;
|
||||
// we are actually interested in the comments, too.
|
||||
// source= removeLeadingComments(source);
|
||||
|
||||
try {
|
||||
delim= StubUtility.getLineDelimiterUsed(curr);
|
||||
} catch (CModelException e) {
|
||||
delim= System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
}
|
||||
String delim= System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
String[] sourceLines= Strings.convertIntoLines(source);
|
||||
String firstLine= sourceLines[0];
|
||||
|
@ -112,12 +480,97 @@ public class CSourceHover extends AbstractCEditorTextHover implements ITextHover
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the source for the given element including the preceding comment.
|
||||
*
|
||||
* @param document the document of the current editor
|
||||
* @param cElement the element to compute the source from
|
||||
* @return the source or <code>null</code>
|
||||
* @throws CModelException
|
||||
* @throws BadLocationException
|
||||
*/
|
||||
private String getSourceForCElement(IDocument doc, ICElement cElement) throws CModelException, BadLocationException {
|
||||
if (!(cElement instanceof ISourceReference)) {
|
||||
return null;
|
||||
}
|
||||
ISourceRange sourceRange= ((ISourceReference)cElement).getSourceRange();
|
||||
int sourceStart= sourceRange.getStartPos();
|
||||
int sourceEnd= sourceStart + sourceRange.getLength();
|
||||
CHeuristicScanner scanner= new CHeuristicScanner(doc);
|
||||
int commentBound= scanner.scanBackward(sourceStart, CHeuristicScanner.UNBOUND, new char[] { '{', '}', ';' });
|
||||
if (commentBound == CHeuristicScanner.NOT_FOUND) {
|
||||
commentBound= -1;
|
||||
}
|
||||
int commentStart= searchCommentBackward(doc, sourceStart, commentBound);
|
||||
if (commentStart >= 0) {
|
||||
sourceStart= commentStart;
|
||||
}
|
||||
return doc.get(sourceStart, sourceEnd - sourceStart + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 -1;
|
||||
}
|
||||
int currentOffset= doc.getLineOffset(firstLine - 1);
|
||||
int commentOffset= -1;
|
||||
while (currentOffset > bound) {
|
||||
ITypedRegion partition= TextUtilities.getPartition(doc, ICPartitions.C_PARTITIONING, currentOffset, false);
|
||||
currentOffset= partition.getOffset() - 1;
|
||||
if (ICPartitions.C_MULTI_LINE_COMMENT.equals(partition.getType())) {
|
||||
int previousCommentOffset= commentOffset;
|
||||
commentOffset = partition.getOffset();
|
||||
final int startLine= doc.getLineOfOffset(commentOffset);
|
||||
final int lineOffset= doc.getLineOffset(startLine);
|
||||
if (commentOffset == lineOffset ||
|
||||
doc.get(lineOffset, commentOffset - lineOffset).trim().length() == 0) {
|
||||
return lineOffset;
|
||||
}
|
||||
return previousCommentOffset;
|
||||
} else if (ICPartitions.C_SINGLE_LINE_COMMENT.equals(partition.getType())) {
|
||||
commentOffset = partition.getOffset();
|
||||
final int startLine= doc.getLineOfOffset(commentOffset);
|
||||
final int lineOffset= doc.getLineOffset(startLine);
|
||||
if (commentOffset == lineOffset ||
|
||||
doc.get(lineOffset, commentOffset - lineOffset).trim().length() == 0) {
|
||||
continue;
|
||||
}
|
||||
return commentOffset;
|
||||
} else if (IDocument.DEFAULT_CONTENT_TYPE.equals(partition.getType())) {
|
||||
if (commentOffset >= 0) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return commentOffset;
|
||||
}
|
||||
|
||||
private static int getTabWidth() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
|
||||
private String removeLeadingComments(String source) {
|
||||
/**
|
||||
* Strip the leading comment from the given source string.
|
||||
*
|
||||
* @param source
|
||||
* @return the source string without leading comments
|
||||
*/
|
||||
protected static String removeLeadingComments(String source) {
|
||||
CCodeReader reader= new CCodeReader();
|
||||
IDocument document= new Document(source);
|
||||
int i;
|
||||
|
@ -145,33 +598,38 @@ public class CSourceHover extends AbstractCEditorTextHover implements ITextHover
|
|||
return source.substring(i);
|
||||
}
|
||||
|
||||
private String findMatches(String name, ITextViewer textViewer) {
|
||||
private String searchInIndex(String name, IRegion textRegion) {
|
||||
//Before trying a search lets make sure that the user is not hovering over a keyword
|
||||
if (selectionIsKeyword(name))
|
||||
return null;
|
||||
|
||||
IEditorPart editor = getEditor();
|
||||
if (editor != null) {
|
||||
IEditorInput input= editor.getEditorInput();
|
||||
IWorkingCopyManager manager= CUIPlugin.getDefault().getWorkingCopyManager();
|
||||
IWorkingCopy copy = manager.getWorkingCopy(input);
|
||||
|
||||
//Before trying a search lets make sure that the user is not hovering over a keyword (which will
|
||||
//result in null being returned)
|
||||
if (selectionIsKeyword(name))
|
||||
return null;
|
||||
IWorkingCopy copy= manager.getWorkingCopy(input);
|
||||
|
||||
if (copy != null) {
|
||||
// TODO actually find some matches...
|
||||
IProgressMonitor monitor= new NullProgressMonitor();
|
||||
ComputeSourceRunnable computer= new ComputeSourceRunnable(copy, textRegion);
|
||||
ASTProvider.getASTProvider().runOnAST(copy, ASTProvider.WAIT_ACTIVE_ONLY, monitor, computer);
|
||||
return computer.getSource();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 keywords =ParserFactory.getKeywordSet(KeywordSetKey.KEYWORDS, ParserLanguage.CPP );
|
||||
if (keywords.contains(name))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
Set keywords= ParserFactory.getKeywordSet(KeywordSetKey.KEYWORDS, ParserLanguage.CPP);
|
||||
return keywords.contains(name);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
Loading…
Add table
Reference in a new issue