/*******************************************************************************
* Copyright (c) 2007, 2015 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* QNX - Initial API and implementation
* Markus Schorn (Wind River Systems)
* Anton Leherbauer (Wind River Systems)
* Sergey Prigogin (Google)
* Jens Elmenthaler - http://bugs.eclipse.org/173458 (camel case completion)
* Nathan Ridge
* Thomas Corbat (IFS)
* Mohamed Azab (Mentor Graphics) - Bug 438549. Add mechanism for parameter guessing.
*******************************************************************************/
package org.eclipse.cdt.internal.ui.text.contentassist;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jface.preference.IPreferenceStore;
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.dom.ILinkage;
import org.eclipse.cdt.core.dom.ast.ASTTypeUtil;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
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.IASTFunctionStyleMacroParameter;
import org.eclipse.cdt.core.dom.ast.IASTIdExpression;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNameOwner;
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.IASTStatement;
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.IField;
import org.eclipse.cdt.core.dom.ast.IFunction;
import org.eclipse.cdt.core.dom.ast.IFunctionType;
import org.eclipse.cdt.core.dom.ast.IParameter;
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.IVariable;
import org.eclipse.cdt.core.dom.ast.c.ICFunctionScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDirective;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPAliasTemplate;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBlockScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassTemplate;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassTemplatePartialSpecialization;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPField;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunctionScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunctionTemplate;
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.ICPPParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateArgument;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateDefinition;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateNonTypeParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateTemplateParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateTypeParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPUsingDeclaration;
import org.eclipse.cdt.core.parser.IToken;
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.ASTNode;
import org.eclipse.cdt.internal.core.dom.parser.ASTQueries;
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.c.CImplicitTypedef;
import org.eclipse.cdt.internal.core.dom.parser.c.CVisitor;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTQualifiedName;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPBuiltinParameter;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPBuiltinVariable;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPImplicitFunction;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPImplicitMethod;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPImplicitTypedef;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ClassTypeHelper;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.AccessContext;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics;
import org.eclipse.cdt.internal.core.parser.scanner.TokenWithImage;
import org.eclipse.cdt.internal.core.parser.util.ContentAssistMatcherFactory;
import org.eclipse.cdt.internal.ui.viewsupport.CElementImageProvider;
/**
* Searches the DOM (both the AST and the index) for completion proposals.
*
* @author Bryan Wilkinson
*/
public class DOMCompletionProposalComputer extends ParsingBasedProposalComputer {
private static final String HASH = "#"; //$NON-NLS-1$;
private static final String DEFAULT_ARGUMENT_PATTERN = " = {0}"; //$NON-NLS-1$;
private static final String TEMPLATE_PARAMETER_PATTERN = "template<{0}> class"; //$NON-NLS-1$;
private static final String TYPENAME = "typename"; //$NON-NLS-1$;
private static final String ELLIPSIS = "..."; //$NON-NLS-1$;
private String fPrefix = ""; //$NON-NLS-1$
private boolean fGuessArguments;
private List<IBinding> fAvailableElements;
/**
* Default constructor is required (executable extension).
*/
public DOMCompletionProposalComputer() {
}
@Override
protected List<ICompletionProposal> computeCompletionProposals(CContentAssistInvocationContext context,
IASTCompletionNode completionNode, String prefix) {
fPrefix = prefix;
fGuessArguments = getPreferenceStore().getBoolean(ContentAssistPreference.GUESS_ARGUMENTS);
fAvailableElements = fGuessArguments ? getDefinedElements(context) : Collections.<IBinding>emptyList();
List<ICompletionProposal> proposals = new ArrayList<>();
if (inPreprocessorDirective(context)) {
if (!inPreprocessorKeyword(context)) {
// Add only macros.
if (prefix.length() == 0) {
try {
prefix= context.computeIdentifierPrefix().toString();
} catch (BadLocationException exc) {
CUIPlugin.log(exc);
}
}
addMacroProposals(context, prefix, proposals);
}
} else {
boolean handleMacros= false;
IASTName[] names = completionNode.getNames();
for (IASTName name : names) {
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;
} else if (astContext instanceof IASTIdExpression
|| astContext instanceof IASTNamedTypeSpecifier) {
// Handle macros only if there is a prefix.
handleMacros = prefix.length() > 0;
}
IBinding[] bindings = astContext.findBindings(name, !context.isContextInformationStyle());
if (bindings != null) {
AccessContext accessibilityContext = new AccessContext(name);
for (IBinding binding : bindings) {
if (accessibilityContext.isAccessible(binding))
handleBinding(binding, context, prefix, astContext, proposals);
}
}
}
if (handleMacros)
addMacroProposals(context, prefix, proposals);
}
return proposals;
}
/**
* Checks whether the invocation offset is inside or before the preprocessor directive keyword.
*
* @param context the invocation context
* @return {@code true} if the invocation offset is inside or before the directive keyword
*/
private boolean inPreprocessorKeyword(CContentAssistInvocationContext context) {
IDocument doc = context.getDocument();
int offset = context.getInvocationOffset();
try {
final ITypedRegion partition=
TextUtilities.getPartition(doc, ICPartitions.C_PARTITIONING, offset, true);
if (ICPartitions.C_PREPROCESSOR.equals(partition.getType())) {
String ppPrefix= doc.get(partition.getOffset(), offset - partition.getOffset());
if (ppPrefix.matches("\\s*#\\s*\\w*")) { //$NON-NLS-1$
// We are inside the directive keyword.
return true;
}
}
} catch (BadLocationException e) {
}
return false;
}
/**
* Checks if the invocation offset is inside a preprocessor directive.
*
* @param context the content assist invocation context
* @return {@code true} if invocation offset is inside a preprocessor directive
*/
private boolean inPreprocessorDirective(CContentAssistInvocationContext context) {
IDocument doc = context.getDocument();
int offset = context.getInvocationOffset();
try {
final ITypedRegion partition=
TextUtilities.getPartition(doc, ICPartitions.C_PARTITIONING, offset, true);
if (ICPartitions.C_PREPROCESSOR.equals(partition.getType())) {
return true;
}
} catch (BadLocationException exc) {
}
return false;
}
private void addMacroProposals(CContentAssistInvocationContext context, String prefix,
List<ICompletionProposal> proposals) {
IASTCompletionNode completionNode = context.getCompletionNode();
addMacroProposals(context, prefix, proposals,
completionNode.getTranslationUnit().getMacroDefinitions());
addMacroProposals(context, prefix, proposals,
completionNode.getTranslationUnit().getBuiltinMacroDefinitions());
}
private void addMacroProposals(CContentAssistInvocationContext context, String prefix,
List<ICompletionProposal> proposals, IASTPreprocessorMacroDefinition[] macros) {
if (macros != null) {
char[] prefixChars= prefix.toCharArray();
final boolean matchPrefix= !context.isContextInformationStyle();
if (matchPrefix) {
IContentAssistMatcher matcher =
ContentAssistMatcherFactory.getInstance().createMatcher(prefixChars);
for (int i = 0; i < macros.length; ++i) {
final char[] macroName= macros[i].getName().toCharArray();
if (matcher.match(macroName)) {
handleMacro(macros[i], context, prefix, proposals);
}
}
} else {
for (int i = 0; i < macros.length; ++i) {
final char[] macroName= macros[i].getName().toCharArray();
if (CharArrayUtils.equals(macroName, 0, macroName.length, prefixChars, true)) {
handleMacro(macros[i], context, prefix, proposals);
}
}
}
}
}
private void handleMacro(IASTPreprocessorMacroDefinition macro, CContentAssistInvocationContext context,
String prefix, List<ICompletionProposal> proposals) {
final String macroName = macro.getName().toString();
final int baseRelevance= computeBaseRelevance(prefix, macroName);
Image image = getImage(CElementImageProvider.getMacroImageDescriptor());
if (macro instanceof IASTPreprocessorFunctionStyleMacroDefinition) {
IASTPreprocessorFunctionStyleMacroDefinition functionMacro =
(IASTPreprocessorFunctionStyleMacroDefinition) macro;
StringBuilder repStringBuff = new StringBuilder();
repStringBuff.append(macroName);
repStringBuff.append('(');
StringBuilder args = new StringBuilder();
IASTFunctionStyleMacroParameter[] params = functionMacro.getParameters();
if (params != null) {
final String parameterDelimiter = context.getFunctionParameterDelimiter();
for (int i = 0; i < params.length; ++i) {
if (i > 0) {
args.append(parameterDelimiter);
}
args.append(params[i].getParameter());
}
}
String argString = args.toString();
StringBuilder descStringBuff = new StringBuilder(repStringBuff);
descStringBuff.append(argString);
descStringBuff.append(')');
repStringBuff.append(')');
String repString = repStringBuff.toString();
String descString = descStringBuff.toString();
CCompletionProposal proposal = createProposal(repString, descString, prefix.length(), image,
baseRelevance + RelevanceConstants.MACRO_TYPE_RELEVANCE, context);
if (!context.isContextInformationStyle()) {
if (argString.length() > 0) {
proposal.setCursorPosition(repString.length() - 1);
} else {
proposal.setCursorPosition(repString.length());
}
}
if (!argString.isEmpty()) {
CProposalContextInformation info =
new CProposalContextInformation(image, descString, argString);
info.setContextInformationPosition(context.getContextInformationOffset());
proposal.setContextInformation(info);
}
proposals.add(proposal);
} else {
proposals.add(createProposal(macroName, macroName, prefix.length(), image,
baseRelevance + RelevanceConstants.MACRO_TYPE_RELEVANCE, context));
}
}
protected void handleBinding(IBinding binding, CContentAssistInvocationContext cContext, String prefix,
IASTCompletionContext astContext, List<ICompletionProposal> proposals) {
if ((binding instanceof CPPImplicitFunction
|| binding instanceof CPPImplicitTypedef
|| binding instanceof CPPBuiltinVariable
|| binding instanceof CPPBuiltinParameter
|| binding instanceof CImplicitFunction
|| binding instanceof CImplicitTypedef
|| binding instanceof CBuiltinVariable
|| binding instanceof CBuiltinParameter
|| binding instanceof ICPPClassTemplatePartialSpecialization)
&& !(binding instanceof CPPImplicitMethod)) {
return;
}
if (!isAnonymousBinding(binding)) {
final String name = binding.getName();
final int baseRelevance= computeBaseRelevance(prefix, name);
if (binding instanceof ICPPClassType) {
handleClass((ICPPClassType) binding, astContext, cContext, baseRelevance, proposals);
} else if (binding instanceof ICPPAliasTemplate) {
handleAliasTemplate((ICPPAliasTemplate) binding, cContext, baseRelevance, proposals);
} else if (binding instanceof IFunction) {
handleFunction((IFunction) binding, astContext, cContext, baseRelevance, proposals);
} else if (binding instanceof IVariable) {
handleVariable((IVariable) binding, astContext, cContext, baseRelevance, proposals);
} else if (!cContext.isContextInformationStyle()) {
if (binding instanceof ITypedef) {
proposals.add(createProposal(name, name, getImage(binding),
baseRelevance + RelevanceConstants.TYPEDEF_TYPE_RELEVANCE, cContext));
} else if (binding instanceof ICPPNamespace) {
handleNamespace((ICPPNamespace) binding, astContext, cContext, baseRelevance, proposals);
} else if (binding instanceof IEnumeration) {
proposals.add(createProposal(name, name, getImage(binding),
baseRelevance + RelevanceConstants.ENUMERATION_TYPE_RELEVANCE, cContext));
} else if (binding instanceof IEnumerator) {
proposals.add(createProposal(name, name, getImage(binding),
baseRelevance + RelevanceConstants.ENUMERATOR_TYPE_RELEVANCE, cContext));
} else {
proposals.add(createProposal(name, name, getImage(binding),
baseRelevance + RelevanceConstants.DEFAULT_TYPE_RELEVANCE, cContext));
}
}
}
}
private boolean isAnonymousBinding(IBinding binding) {
char[] name= binding.getNameCharArray();
return name.length == 0 || name[0] == '{';
}
private void addProposalForClassTemplate(ICPPClassTemplate templateType,
CContentAssistInvocationContext context, int baseRelevance, List<ICompletionProposal> proposals) {
int relevance = getClassTypeRelevance(templateType);
addProposalForTemplateDefinition(templateType, context, baseRelevance + relevance, proposals);
}
private void addProposalForTemplateDefinition(ICPPTemplateDefinition templateType,
CContentAssistInvocationContext context, int relevance, List<ICompletionProposal> proposals) {
StringBuilder representation = new StringBuilder(templateType.getName());
boolean inUsingDeclaration = context.isInUsingDirective();
String templateParameterRepresentation = ""; //$NON-NLS-1$
if (!inUsingDeclaration) {
representation.append("<{0}>"); //$NON-NLS-1$
templateParameterRepresentation = buildTemplateParameters(templateType, context);
} else if (!context.isFollowedBySemicolon()) {
representation.append(';');
}
String representationString = MessageFormat.format(representation.toString(), ""); //$NON-NLS-1$
String displayString = MessageFormat.format(representation.toString(), templateParameterRepresentation);
CCompletionProposal proposal = createProposal(representationString, displayString, getImage(templateType),
relevance, context);
if (!inUsingDeclaration) {
CProposalContextInformation info = new CProposalContextInformation(
getImage(templateType), displayString, templateParameterRepresentation);
info.setContextInformationPosition(context.getContextInformationOffset());
proposal.setContextInformation(info);
if (!context.isContextInformationStyle()) {
proposal.setCursorPosition(representationString.length() - 1);
}
}
proposals.add(proposal);
}
private String buildTemplateParameters(ICPPTemplateDefinition templateType, CContentAssistInvocationContext context) {
ICPPTemplateParameter[] parameters = templateType.getTemplateParameters();
StringBuilder representation = new StringBuilder();
final String parameterDelimiter = context.getTemplateParameterDelimiter();
final boolean addDefaultedParameters = isDisplayDefaultedParameters();
final boolean addDefaultArguments = isDisplayDefaultArguments();
for (int i = 0; i < parameters.length; i++) {
ICPPTemplateParameter parameter = parameters[i];
ICPPTemplateArgument defaultValue = parameter.getDefaultValue();
if (!addDefaultedParameters && defaultValue != null) {
break;
}
if (i > 0) {
representation.append(parameterDelimiter);
}
if (parameter instanceof ICPPTemplateNonTypeParameter) {
IType parameterType = ((ICPPTemplateNonTypeParameter) parameter).getType();
String typeName = ASTTypeUtil.getType(parameterType);
representation.append(typeName);
} else if (parameter instanceof ICPPTemplateTypeParameter) {
representation.append(TYPENAME);
} else if (parameter instanceof ICPPTemplateTemplateParameter) {
String templateParameterParameters =
buildTemplateParameters((ICPPTemplateTemplateParameter) parameter, context);
representation.append(
MessageFormat.format(TEMPLATE_PARAMETER_PATTERN, templateParameterParameters));
representation.append(templateParameterParameters);
}
if (parameter.isParameterPack()) {
representation.append(ELLIPSIS);
}
representation.append(' ');
representation.append(parameter.getName());
if (addDefaultArguments && defaultValue != null) {
String defaultArgumentRepresentation =
MessageFormat.format(DEFAULT_ARGUMENT_PATTERN, defaultValue);
for (int parameterIndex = 0; parameterIndex < i; parameterIndex++) {
String templateArgumentID = HASH + parameterIndex;
String templateArgumentValue = parameters[parameterIndex].getName();
defaultArgumentRepresentation =
defaultArgumentRepresentation.replaceAll(templateArgumentID, templateArgumentValue);
}
representation.append(defaultArgumentRepresentation);
}
}
return representation.toString();
}
private void handleClass(ICPPClassType classType, IASTCompletionContext astContext,
CContentAssistInvocationContext cContext, int baseRelevance, List<ICompletionProposal> proposals) {
if (cContext.isContextInformationStyle() && cContext.isAfterOpeningParenthesis()) {
addProposalsForConstructors(classType, astContext, cContext, baseRelevance, proposals);
} else if (classType instanceof ICPPClassTemplate) {
addProposalForClassTemplate((ICPPClassTemplate) classType, cContext, baseRelevance, proposals);
} else {
int relevance = getClassTypeRelevance(classType);
if (astContext instanceof IASTName && !(astContext instanceof ICPPASTQualifiedName)) {
IASTName name= (IASTName)astContext;
if (name.getParent() instanceof IASTDeclarator) {
proposals.add(createProposal(classType.getName() + "::", classType.getName(), //$NON-NLS-1$
getImage(classType), baseRelevance + relevance, cContext));
}
}
proposals.add(createProposal(classType.getName(), classType.getName(), getImage(classType),
baseRelevance + RelevanceConstants.CLASS_TYPE_RELEVANCE, cContext));
}
}
private void handleAliasTemplate(ICPPAliasTemplate aliasTemplate,
CContentAssistInvocationContext context, int baseRelevance, List<ICompletionProposal> proposals) {
addProposalForTemplateDefinition(aliasTemplate, context,
baseRelevance + RelevanceConstants.TYPEDEF_TYPE_RELEVANCE, proposals);
}
private void addProposalsForConstructors(ICPPClassType classType, IASTCompletionContext astContext,
CContentAssistInvocationContext cContext, int baseRelevance, List<ICompletionProposal> proposals) {
ICPPConstructor[] constructors = classType.getConstructors();
for (ICPPConstructor constructor : constructors) {
handleFunction(constructor, astContext, cContext, baseRelevance, proposals);
}
}
private int getClassTypeRelevance(ICPPClassType classType) {
int relevance= 0;
switch (classType.getKey()) {
case ICPPClassType.k_class:
relevance= RelevanceConstants.CLASS_TYPE_RELEVANCE;
break;
case ICompositeType.k_struct:
relevance= RelevanceConstants.STRUCT_TYPE_RELEVANCE;
break;
case ICompositeType.k_union:
relevance= RelevanceConstants.UNION_TYPE_RELEVANCE;
break;
}
return relevance;
}
// Returns whether a function name being completed could be a call that function.
private boolean canBeCall(IFunction function, IASTCompletionContext astContext,
CContentAssistInvocationContext cContext) {
// Can't have a call in a using-directive.
if (cContext.isInUsingDirective()) {
return false;
}
// Otherwise, it can be call unless the function is a nonstatic method,
// and we are not inside the class's scope.
if (astContext instanceof CPPASTQualifiedName) {
CPPASTQualifiedName qname = (CPPASTQualifiedName) astContext;
if (!function.isStatic() && !CPPASTQualifiedName.canBeFieldAccess(qname)) {
return false;
}
}
return true;
}
private void handleFunction(IFunction function, IASTCompletionContext astContext,
CContentAssistInvocationContext cContext, int baseRelevance, List<ICompletionProposal> proposals) {
Image image = getImage(function);
StringBuilder repStringBuff = new StringBuilder();
repStringBuff.append(function.getName());
boolean canBeCall = canBeCall(function, astContext, cContext);
repStringBuff.append('(');
StringBuilder dispArgs = new StringBuilder(); // For the dispArgString
StringBuilder idArgs = new StringBuilder(); // For the idArgString
boolean hasArgs = true;
String returnTypeStr = null;
IParameter[] params = function.getParameters();
if (params != null) {
final String parameterDelimiter = cContext.getFunctionParameterDelimiter();
for (int i = 0; i < params.length; ++i) {
IParameter param = params[i];
if (skipDefaultedParameter(param)) {
break;
}
IType paramType = param.getType();
if (i > 0) {
dispArgs.append(parameterDelimiter);
idArgs.append(parameterDelimiter);
}
String paramTypeString = ASTTypeUtil.getType(paramType, false);
dispArgs.append(paramTypeString);
idArgs.append(paramTypeString);
String paramName = param.getName();
if (paramName != null && paramName.length() > 0) {
dispArgs.append(' ');
dispArgs.append(paramName);
}
if (param instanceof ICPPParameter) {
ICPPParameter cppParam = (ICPPParameter) param;
if (cppParam.hasDefaultValue() && isDisplayDefaultArguments()) {
dispArgs.append(
MessageFormat.format(DEFAULT_ARGUMENT_PATTERN, cppParam.getDefaultValue()));
}
}
}
if (function.takesVarArgs()) {
if (params.length != 0) {
dispArgs.append(parameterDelimiter);
idArgs.append(parameterDelimiter);
}
dispArgs.append("..."); //$NON-NLS-1$
idArgs.append("..."); //$NON-NLS-1$
} else if (params.length == 0) { // force the void in
dispArgs.append("void"); //$NON-NLS-1$
idArgs.append("void"); //$NON-NLS-1$
}
}
IFunctionType functionType = function.getType();
if (functionType != null) {
IType returnType = functionType.getReturnType();
if (returnType != null)
returnTypeStr = ASTTypeUtil.getType(returnType, false);
}
hasArgs = ASTTypeUtil.functionTakesParameters(function);
String dispArgString = dispArgs.toString();
String idArgString = idArgs.toString();
String contextDispargString = hasArgs ? dispArgString : null;
StringBuilder dispStringBuff = new StringBuilder(repStringBuff);
dispStringBuff.append(dispArgString);
dispStringBuff.append(')');
if (returnTypeStr != null && !returnTypeStr.isEmpty()) {
dispStringBuff.append(" : "); //$NON-NLS-1$
dispStringBuff.append(returnTypeStr);
}
String dispString = dispStringBuff.toString();
StringBuilder idStringBuff = new StringBuilder(repStringBuff);
idStringBuff.append(idArgString);
idStringBuff.append(')');
String idString = idStringBuff.toString();
boolean inUsingDeclaration = cContext.isInUsingDirective();
// If we can't be calling the function in this context, do not
// emit parentheses, since the user will just have to delete them.
if (!canBeCall) {
repStringBuff.setLength(repStringBuff.length() - 1); // Remove opening parenthesis
// In a using declaration, emitting a semicolon instead is useful.
if (inUsingDeclaration && !cContext.isFollowedBySemicolon()) {
repStringBuff.append(';');
}
} else {
repStringBuff.append(')');
}
String repString = repStringBuff.toString();
final int relevance = function instanceof ICPPMethod ?
RelevanceConstants.METHOD_TYPE_RELEVANCE : RelevanceConstants.FUNCTION_TYPE_RELEVANCE;
CCompletionProposal proposal = createProposal(repString, dispString, idString,
cContext.getCompletionNode().getLength(), image, baseRelevance + relevance, cContext);
if (!cContext.isContextInformationStyle()) {
int cursorPosition = !inUsingDeclaration && hasArgs ?
repString.length() - 1 : repString.length();
proposal.setCursorPosition(cursorPosition);
}
if (contextDispargString != null && !inUsingDeclaration) {
CProposalContextInformation info =
new CProposalContextInformation(image, dispString, contextDispargString);
info.setContextInformationPosition(cContext.getContextInformationOffset());
proposal.setContextInformation(info);
}
// The ParameterGuessingProposal will be active if the function accepts parameters and the content
// assist is invoked before typing any parameters. Otherwise, the normal parameter hint proposal will
// be added.
if (function.getParameters() != null && function.getParameters().length != 0
&& isBeforeParameters(cContext)) {
proposals.add(ParameterGuessingProposal.createProposal(cContext, fAvailableElements, proposal,
function, fPrefix, fGuessArguments));
} else {
proposals.add(proposal);
}
}
/**
* Returns true if the invocation is at the function name or before typing any parameters.
*/
private boolean isBeforeParameters(CContentAssistInvocationContext context) {
// Invocation offset and parse offset are the same if content assist is invoked while in the function
// name (i.e. before the '('). After that, the parse offset will indicate the end of the name part.
// If there is no difference between them, then we're still inside the function name part.
int relativeOffset = context.getInvocationOffset() - context.getParseOffset();
if (relativeOffset == 0)
return true;
int startOffset = context.getParseOffset();
String completePrefix = context.getDocument().get().substring(startOffset,
context.getInvocationOffset());
int lastChar = getLastNonWhitespaceChar(completePrefix);
if (lastChar != -1 && completePrefix.charAt(lastChar) == '(')
return true;
return false;
}
private static int getLastNonWhitespaceChar(String str) {
char[] chars = str.toCharArray();
for (int i = chars.length - 1; i >= 0; i--) {
if (!Character.isWhitespace(chars[i]))
return i;
}
return -1;
}
/**
* Initializes the list of variables accessible at the start of the current statement.
*/
private List<IBinding> getDefinedElements(CContentAssistInvocationContext context) {
// Get all variables accessible at the start of the statement.
// ex1: int a = foo(
// ^ --> We don't want 'a' as a suggestion.
// ex2: char* foo(int a, int b) { return NULL; }
// void bar(char* name) {}
// ...
// bar( foo(
// ^ --> If this offset is used, the only defined name will be "bar(char*)".
IASTCompletionNode node = context.getCompletionNode();
if (node == null)
return Collections.emptyList();
// Find the enclosing statement at the point of completion.
IASTStatement completionStatement = null;
IASTName[] completionNames = node.getNames();
for (IASTName name : completionNames) {
IASTStatement statement = ASTQueries.findAncestorWithType(name, IASTStatement.class);
if (statement != null && statement.getParent() != null) {
if (completionStatement == null
|| getNodeOffset(statement) < getNodeOffset(completionStatement)) {
completionStatement = statement;
}
}
}
if (completionStatement == null)
return Collections.emptyList();
// Get content assist results for an empty prefix at the start of the statement.
final int statementOffset = getNodeOffset(completionStatement);
IToken token = new TokenWithImage(IToken.tCOMPLETION, null, statementOffset, statementOffset,
CharArrayUtils.EMPTY_CHAR_ARRAY);
IASTTranslationUnit ast = node.getTranslationUnit();
IASTName name = ast.getASTNodeFactory().newName(token.getCharImage());
((ASTNode) name).setOffsetAndLength(token.getOffset(), 0);
name.setParent(completionStatement);
IBinding[] bindings = findBindingsForContextAssist(name, ast);
if (bindings.length == 0)
return Collections.emptyList();
// Get all variables declared in the translation unit.
final Set<IBinding> declaredVariables = new HashSet<>();
ast.accept(new ASTVisitor(true) {
@Override
public int visit(IASTName name) {
if (getNodeOffset(name) >= statementOffset)
return PROCESS_ABORT;
int role = name.getRoleOfName(true);
if (role == IASTNameOwner.r_declaration || role == IASTNameOwner.r_definition) {
IBinding binding = name.resolveBinding();
if (binding instanceof IVariable) {
declaredVariables.add(binding);
}
}
return PROCESS_SKIP; // Do non visit internals of qualified names.
}
});
Map<String, IBinding> elementsMap = new HashMap<>();
AccessContext accessibilityContext = new AccessContext(name);
for (IBinding binding : bindings) {
// Consider only fields and variables that are declared in the current translation unit.
if (binding instanceof IVariable
&& !elementsMap.containsKey(binding.getName())
&& (binding instanceof ICPPField || declaredVariables.contains(binding))
&& accessibilityContext.isAccessible(binding)) {
elementsMap.put(binding.getName(), binding);
}
}
return new ArrayList<>(elementsMap.values());
}
private IBinding[] findBindingsForContextAssist(IASTName name, IASTTranslationUnit ast) {
if (ast.getLinkage().getLinkageID() == ILinkage.CPP_LINKAGE_ID)
return CPPSemantics.findBindingsForContentAssist(name, true, new String[0]);
return CVisitor.findBindingsForContentAssist(name, true);
}
private int getNodeOffset(IASTNode node) {
return ((ASTNode) node).getOffset();
}
private boolean skipDefaultedParameter(IParameter param) {
return !isDisplayDefaultedParameters() && param instanceof ICPPParameter && ((ICPPParameter)param).hasDefaultValue();
}
private void handleVariable(IVariable variable, IASTCompletionContext astContext,
CContentAssistInvocationContext cContext,
int baseRelevance, List<ICompletionProposal> proposals) {
if (cContext.isContextInformationStyle()) {
IType t = variable.getType();
t= unwindTypedefs(t);
if (t instanceof ICPPClassType) {
ICPPClassType classType= (ICPPClassType) t;
IASTTranslationUnit ast = cContext.getCompletionNode().getTranslationUnit();
ICPPConstructor[] constructors = ClassTypeHelper.getConstructors(classType, ast);
for (ICPPConstructor constructor : constructors) {
handleFunction(constructor, astContext, cContext, baseRelevance, proposals);
}
}
return;
}
StringBuilder repStringBuff = new StringBuilder();
repStringBuff.append(variable.getName());
String returnTypeStr = "<unknown>"; //$NON-NLS-1$
IType varType = variable.getType();
if (varType != null)
returnTypeStr = ASTTypeUtil.getType(varType, false);
StringBuilder dispStringBuff = new StringBuilder(repStringBuff);
if (returnTypeStr != null) {
dispStringBuff.append(" : "); //$NON-NLS-1$
dispStringBuff.append(returnTypeStr);
}
String dispString = dispStringBuff.toString();
StringBuilder idStringBuff = new StringBuilder(repStringBuff);
String idString = idStringBuff.toString();
String repString = repStringBuff.toString();
Image image = getImage(variable);
final int relevance = isLocalVariable(variable) ?
RelevanceConstants.LOCAL_VARIABLE_TYPE_RELEVANCE :
isField(variable) ?
RelevanceConstants.FIELD_TYPE_RELEVANCE :
RelevanceConstants.VARIABLE_TYPE_RELEVANCE;
CCompletionProposal proposal = createProposal(repString, dispString, idString,
cContext.getCompletionNode().getLength(), image, baseRelevance + relevance, cContext);
proposals.add(proposal);
}
private IType unwindTypedefs(final IType t) {
IType r= t;
while (r instanceof ITypedef) {
r= ((ITypedef) r).getType();
}
return r != null ? r : t;
}
private static boolean isField(IVariable variable) {
return variable instanceof IField;
}
private static boolean isLocalVariable(IVariable variable) {
try {
return isLocalScope(variable.getScope());
} catch (DOMException exc) {
return false;
}
}
private static boolean isLocalScope(IScope scope) {
while (scope != null) {
if (scope instanceof ICPPFunctionScope ||
scope instanceof ICPPBlockScope ||
scope instanceof ICFunctionScope) {
return true;
}
try {
scope= scope.getParent();
} catch (DOMException e) {
scope= null;
}
}
return false;
}
private void handleNamespace(ICPPNamespace namespace, IASTCompletionContext astContext,
CContentAssistInvocationContext cContext, int baseRelevance,
List<ICompletionProposal> proposals) {
if (astContext instanceof ICPPASTQualifiedName) {
IASTCompletionContext parent = ((ICPPASTQualifiedName) astContext).getCompletionContext();
handleNamespace(namespace, parent, cContext, baseRelevance, proposals);
return;
}
StringBuilder repStringBuff = new StringBuilder();
repStringBuff.append(namespace.getName());
if (!(astContext instanceof ICPPASTUsingDeclaration)
&& !(astContext instanceof ICPPASTUsingDirective)) {
repStringBuff.append("::"); //$NON-NLS-1$
}
String repString = repStringBuff.toString();
proposals.add(createProposal(repString, namespace.getName(), getImage(namespace),
baseRelevance + RelevanceConstants.NAMESPACE_TYPE_RELEVANCE, cContext));
}
private CCompletionProposal createProposal(String repString, String dispString, Image image,
int relevance, CContentAssistInvocationContext context) {
return createProposal(repString, dispString, null, context.getCompletionNode().getLength(), image,
relevance, context);
}
private CCompletionProposal createProposal(String repString, String dispString, int prefixLength,
Image image, int relevance, CContentAssistInvocationContext context) {
return createProposal(repString, dispString, null, prefixLength, image, relevance, context);
}
private CCompletionProposal createProposal(String repString, String dispString, String idString,
int prefixLength, Image image, int relevance, CContentAssistInvocationContext context) {
int parseOffset = context.getParseOffset();
int invocationOffset = context.getInvocationOffset();
boolean doReplacement = !context.isContextInformationStyle();
int repLength = doReplacement ? prefixLength : 0;
int repOffset = doReplacement ? parseOffset - repLength : invocationOffset;
repString = doReplacement ? repString : ""; //$NON-NLS-1$
return new CCompletionProposal(repString, repOffset, repLength, image, dispString, idString,
relevance, context.getViewer());
}
private Image getImage(ImageDescriptor desc) {
return desc != null ? CUIPlugin.getImageDescriptorRegistry().get(desc) : null;
}
private Image getImage(IBinding binding) {
ImageDescriptor imageDescriptor = null;
if (binding instanceof ITypedef || binding instanceof ICPPAliasTemplate) {
imageDescriptor = CElementImageProvider.getTypedefImageDescriptor();
} else if (binding instanceof ICompositeType) {
if (((ICompositeType)binding).getKey() == ICPPClassType.k_class || binding instanceof ICPPClassTemplate)
imageDescriptor = CElementImageProvider.getClassImageDescriptor();
else if (((ICompositeType)binding).getKey() == ICompositeType.k_struct)
imageDescriptor = CElementImageProvider.getStructImageDescriptor();
else if (((ICompositeType)binding).getKey() == ICompositeType.k_union)
imageDescriptor = CElementImageProvider.getUnionImageDescriptor();
} else if (binding instanceof ICPPMethod) {
switch (((ICPPMethod)binding).getVisibility()) {
case ICPPMember.v_private:
imageDescriptor = CElementImageProvider.getMethodImageDescriptor(ASTAccessVisibility.PRIVATE);
break;
case ICPPMember.v_protected:
imageDescriptor = CElementImageProvider.getMethodImageDescriptor(ASTAccessVisibility.PROTECTED);
break;
default:
imageDescriptor = CElementImageProvider.getMethodImageDescriptor(ASTAccessVisibility.PUBLIC);
break;
}
} else if (binding instanceof IFunction) {
imageDescriptor = CElementImageProvider.getFunctionImageDescriptor();
} else if (binding instanceof ICPPField) {
switch (((ICPPField)binding).getVisibility()) {
case ICPPMember.v_private:
imageDescriptor = CElementImageProvider.getFieldImageDescriptor(ASTAccessVisibility.PRIVATE);
break;
case ICPPMember.v_protected:
imageDescriptor = CElementImageProvider.getFieldImageDescriptor(ASTAccessVisibility.PROTECTED);
break;
default:
imageDescriptor = CElementImageProvider.getFieldImageDescriptor(ASTAccessVisibility.PUBLIC);
break;
}
} else if (binding instanceof IField) {
imageDescriptor = CElementImageProvider.getFieldImageDescriptor(ASTAccessVisibility.PUBLIC);
} else if (binding instanceof IVariable) {
imageDescriptor = CElementImageProvider.getVariableImageDescriptor();
} else if (binding instanceof IEnumeration) {
imageDescriptor = CElementImageProvider.getEnumerationImageDescriptor();
} else if (binding instanceof IEnumerator) {
imageDescriptor = CElementImageProvider.getEnumeratorImageDescriptor();
} else if (binding instanceof ICPPNamespace) {
imageDescriptor = CElementImageProvider.getNamespaceImageDescriptor();
} else if (binding instanceof ICPPFunctionTemplate) {
imageDescriptor = CElementImageProvider.getFunctionImageDescriptor();
} else if (binding instanceof ICPPUsingDeclaration) {
IBinding[] delegates = ((ICPPUsingDeclaration) binding).getDelegates();
if (delegates.length != 0)
return getImage(delegates[0]);
}
return imageDescriptor != null ?
CUIPlugin.getImageDescriptorRegistry().get(imageDescriptor) : null;
}
private static boolean isDisplayDefaultArguments() {
IPreferenceStore preferenceStore = getPreferenceStore();
return preferenceStore.getBoolean(ContentAssistPreference.DEFAULT_ARGUMENT_DISPLAY_ARGUMENTS);
}
private static boolean isDisplayDefaultedParameters() {
IPreferenceStore preferenceStore = getPreferenceStore();
return preferenceStore.getBoolean(ContentAssistPreference.DEFAULT_ARGUMENT_DISPLAY_PARAMETERS_WITH_DEFAULT_ARGUMENT);
}
private static IPreferenceStore getPreferenceStore() {
return CUIPlugin.getDefault().getPreferenceStore();
}
}