| /******************************************************************************* |
| * Copyright (c) 2007, 2008 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) |
| * IBM Corporation |
| *******************************************************************************/ |
| |
| /* -- ST-Origin -- |
| * Source folder: org.eclipse.cdt.ui/src |
| * Class: org.eclipse.cdt.internal.ui.text.contentassist.DOMCompletionProposalComputer |
| * Version: 1.19 |
| */ |
| |
| package org.eclipse.ptp.internal.rdt.core.contentassist; |
| |
| import java.util.LinkedList; |
| import java.util.List; |
| |
| 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.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.IASTPreprocessorFunctionStyleMacroDefinition; |
| import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroDefinition; |
| 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.ICPPBlockScope; |
| import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassTemplate; |
| 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.ICPPUsingDeclaration; |
| import org.eclipse.cdt.core.model.ICElement; |
| import org.eclipse.cdt.core.parser.util.CharArrayUtils; |
| 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.semantics.CPPTemplates.CPPImplicitFunctionTemplate; |
| |
| /** |
| * Searches the DOM (both the AST and the index) for completion proposals. |
| */ |
| public class CompletionProposalComputer { |
| public List<Proposal> computeCompletionProposals( |
| RemoteContentAssistInvocationContext context, |
| IASTCompletionNode completionNode, String prefix) { |
| |
| List<Proposal> proposals = new LinkedList<Proposal>(); |
| |
| if(context.isInPreprocessorDirective()) { |
| if (!context.inPreprocessorKeyword()) { |
| // add only macros |
| if (prefix.length() == 0) { |
| String computedPrefix= context.computeIdentifierPrefix().toString(); |
| if (computedPrefix != null) { |
| prefix= computedPrefix; |
| } |
| } |
| addMacroProposals(context, prefix, proposals); |
| } |
| } else { |
| boolean handleMacros= false; |
| IASTName[] names = completionNode.getNames(); |
| |
| for (int i = 0; i < names.length; ++i) { |
| if (names[i].getTranslationUnit() == null) |
| // The node isn't properly hooked up, must have backtracked out of this node |
| continue; |
| |
| IASTCompletionContext astContext = names[i].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( |
| names[i], !context.isContextInformationStyle()); |
| |
| if (bindings != null) |
| for (int j = 0; j < bindings.length; ++j) |
| handleBinding(bindings[j], context, prefix, astContext, proposals); |
| } |
| |
| if (handleMacros) |
| addMacroProposals(context, prefix, proposals); |
| } |
| |
| return proposals; |
| } |
| |
| private void addMacroProposals(RemoteContentAssistInvocationContext context, String prefix, List<Proposal> proposals) { |
| char[] prefixChars= prefix.toCharArray(); |
| final boolean matchPrefix= !context.isContextInformationStyle(); |
| IASTCompletionNode completionNode = context.getCompletionNode(); |
| IASTPreprocessorMacroDefinition[] macros = completionNode.getTranslationUnit().getMacroDefinitions(); |
| if (macros != null) |
| for (int i = 0; i < macros.length; ++i) { |
| final char[] macroName= macros[i].getName().toCharArray(); |
| if (CharArrayUtils.equals(macroName, 0, matchPrefix ? prefixChars.length : macroName.length, prefixChars, true)) |
| handleMacro(macros[i], context, prefix, proposals); |
| } |
| macros = completionNode.getTranslationUnit().getBuiltinMacroDefinitions(); |
| if (macros != null) |
| for (int i = 0; i < macros.length; ++i) { |
| final char[] macroName= macros[i].getName().toCharArray(); |
| if (CharArrayUtils.equals(macroName, 0, matchPrefix ? prefixChars.length : macroName.length, prefixChars, true)) |
| handleMacro(macros[i], context, prefix, proposals); |
| } |
| } |
| |
| private void handleMacro(IASTPreprocessorMacroDefinition macro, RemoteContentAssistInvocationContext context, String prefix, List<Proposal> proposals) { |
| final String macroName = macro.getName().toString(); |
| final int baseRelevance= computeBaseRelevance(prefix, macroName); |
| |
| CompletionType type = new CompletionType(ICElement.C_MACRO); |
| |
| 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) |
| for (int i = 0; i < params.length; ++i) { |
| if (i > 0) |
| args.append(", "); //$NON-NLS-1$ |
| args.append(params[i].getParameter()); |
| } |
| String argString = args.toString(); |
| |
| StringBuilder descStringBuff = new StringBuilder(repStringBuff.toString()); |
| descStringBuff.append(argString); |
| descStringBuff.append(')'); |
| |
| repStringBuff.append(')'); |
| String repString = repStringBuff.toString(); |
| String descString = descStringBuff.toString(); |
| |
| Proposal proposal = createProposal(repString, descString, type, baseRelevance + RelevanceConstants.MACRO_TYPE_RELEVANCE, context); |
| if (!context.isContextInformationStyle()) { |
| proposal.setCursorPosition(repString.length() - 1); |
| } |
| |
| if (argString.length() > 0) { |
| RemoteProposalContextInformation info = new RemoteProposalContextInformation(type, descString, argString); |
| info.setContextInformationPosition(context.getContextInformationOffset()); |
| proposal.setContextInformation(info); |
| } |
| |
| proposals.add(proposal); |
| } else |
| proposals.add(createProposal(macroName, macroName, type, baseRelevance + RelevanceConstants.MACRO_TYPE_RELEVANCE, context)); |
| } |
| |
| protected void handleBinding(IBinding binding, |
| RemoteContentAssistInvocationContext cContext, |
| String prefix, |
| IASTCompletionContext astContext, List<Proposal> proposals) { |
| if ((binding instanceof CPPImplicitFunction |
| || binding instanceof CPPImplicitFunctionTemplate || binding instanceof CPPImplicitTypedef) |
| && !(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 IFunction) { |
| handleFunction((IFunction)binding, cContext, baseRelevance, proposals); |
| } else if (!cContext.isContextInformationStyle()) { |
| if (binding instanceof IVariable) { |
| handleVariable((IVariable) binding, cContext, baseRelevance, proposals); |
| } else if (binding instanceof ITypedef) { |
| proposals.add(createProposal(name, name, getElementType(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, getElementType(binding), baseRelevance + RelevanceConstants.ENUMERATION_TYPE_RELEVANCE, cContext)); |
| } else if (binding instanceof IEnumerator) { |
| proposals.add(createProposal(name, name, getElementType(binding), baseRelevance + RelevanceConstants.ENUMERATOR_TYPE_RELEVANCE, cContext)); |
| } else { |
| proposals.add(createProposal(name, name, getElementType(binding), baseRelevance + RelevanceConstants.DEFAULT_TYPE_RELEVANCE, cContext)); |
| } |
| } |
| } |
| } |
| |
| private boolean isAnonymousBinding(IBinding binding) { |
| char[] name= binding.getNameCharArray(); |
| return name.length == 0 || name[0] == '{'; |
| } |
| |
| private void handleClass(ICPPClassType classType, IASTCompletionContext astContext, RemoteContentAssistInvocationContext context, int baseRelevance, List<Proposal> proposals) { |
| if (context.isContextInformationStyle()) { |
| try { |
| ICPPConstructor[] constructors = classType.getConstructors(); |
| for (ICPPConstructor constructor : constructors) { |
| handleFunction(constructor, context, baseRelevance, proposals); |
| } |
| } catch (DOMException e) { |
| } |
| } else { |
| int relevance= 0; |
| try { |
| 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; |
| } |
| } catch (DOMException exc) { |
| } |
| if (astContext instanceof IASTName && !(astContext instanceof ICPPASTQualifiedName)) { |
| IASTName name= (IASTName)astContext; |
| if (name.getParent() instanceof IASTDeclarator) { |
| proposals.add(createProposal(classType.getName()+"::", classType.getName(), getElementType(classType), baseRelevance + relevance, context)); //$NON-NLS-1$ |
| } |
| } |
| proposals.add(createProposal(classType.getName(), classType.getName(), getElementType(classType), baseRelevance + RelevanceConstants.CLASS_TYPE_RELEVANCE, context)); |
| } |
| } |
| |
| private void handleFunction(IFunction function, RemoteContentAssistInvocationContext context, int baseRelevance, List<Proposal> proposals) { |
| CompletionType type = getElementType(function); |
| |
| StringBuilder repStringBuff = new StringBuilder(); |
| repStringBuff.append(function.getName()); |
| repStringBuff.append('('); |
| |
| StringBuilder dispargs = new StringBuilder(); // for the displayString |
| StringBuilder idargs = new StringBuilder(); // for the idString |
| String returnTypeStr = null; |
| try { |
| IParameter[] params = function.getParameters(); |
| if (params != null) { |
| for (int i = 0; i < params.length; ++i) { |
| IType paramType = params[i].getType(); |
| if (i > 0) { |
| dispargs.append(','); |
| idargs.append(','); |
| } |
| |
| dispargs.append(ASTTypeUtil.getType(paramType, false)); |
| idargs.append(ASTTypeUtil.getType(paramType, false)); |
| String paramName = params[i].getName(); |
| if (paramName != null && paramName.length() > 0) { |
| dispargs.append(' '); |
| dispargs.append(paramName); |
| } |
| } |
| |
| if (function.takesVarArgs()) { |
| if (params.length > 0) { |
| dispargs.append(','); |
| idargs.append(','); |
| } |
| 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); |
| } |
| } catch (DOMException e) { |
| } |
| |
| String dispargString = dispargs.toString(); |
| String idargString = idargs.toString(); |
| |
| StringBuilder dispStringBuff = new StringBuilder(repStringBuff.toString()); |
| dispStringBuff.append(dispargString); |
| dispStringBuff.append(')'); |
| if (returnTypeStr != null && returnTypeStr.length() > 0) { |
| dispStringBuff.append(" : "); //$NON-NLS-1$ |
| dispStringBuff.append(returnTypeStr); |
| } |
| String dispString = dispStringBuff.toString(); |
| |
| StringBuilder idStringBuff = new StringBuilder(repStringBuff.toString()); |
| idStringBuff.append(idargString); |
| idStringBuff.append(')'); |
| String idString = idStringBuff.toString(); |
| |
| repStringBuff.append(')'); |
| String repString = repStringBuff.toString(); |
| |
| final int relevance = function instanceof ICPPMethod ? RelevanceConstants.METHOD_TYPE_RELEVANCE : RelevanceConstants.FUNCTION_TYPE_RELEVANCE; |
| Proposal proposal = createProposal(repString, dispString, idString, type, baseRelevance + relevance, context); |
| if (!context.isContextInformationStyle()) { |
| proposal.setCursorPosition(repString.length() - 1); |
| } |
| |
| if (dispargString.length() > 0) { |
| RemoteProposalContextInformation info = new RemoteProposalContextInformation(type, dispString, dispargString); |
| info.setContextInformationPosition(context.getContextInformationOffset()); |
| proposal.setContextInformation(info); |
| } |
| |
| proposals.add(proposal); |
| } |
| |
| private void handleVariable(IVariable variable, RemoteContentAssistInvocationContext context, int baseRelevance, List<Proposal> proposals) { |
| StringBuilder repStringBuff = new StringBuilder(); |
| repStringBuff.append(variable.getName()); |
| |
| String returnTypeStr = "<unknown>"; //$NON-NLS-1$ |
| try { |
| IType varType = variable.getType(); |
| if (varType != null) |
| returnTypeStr = ASTTypeUtil.getType(varType, false); |
| } catch (DOMException e) { |
| } |
| |
| StringBuilder dispStringBuff = new StringBuilder(repStringBuff.toString()); |
| if (returnTypeStr != null) { |
| dispStringBuff.append(" : "); //$NON-NLS-1$ |
| dispStringBuff.append(returnTypeStr); |
| } |
| String dispString = dispStringBuff.toString(); |
| |
| StringBuilder idStringBuff = new StringBuilder(repStringBuff.toString()); |
| String idString = idStringBuff.toString(); |
| |
| String repString = repStringBuff.toString(); |
| |
| CompletionType type = getElementType(variable); |
| final int relevance = isLocalVariable(variable) |
| ? RelevanceConstants.LOCAL_VARIABLE_TYPE_RELEVANCE |
| : isField(variable) |
| ? RelevanceConstants.FIELD_TYPE_RELEVANCE |
| : RelevanceConstants.VARIABLE_TYPE_RELEVANCE; |
| Proposal proposal = createProposal(repString, dispString, idString, type, baseRelevance + relevance, context); |
| proposals.add(proposal); |
| } |
| |
| 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, |
| RemoteContentAssistInvocationContext cContext, |
| int baseRelevance, |
| List<Proposal> 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(), getElementType(namespace), baseRelevance + RelevanceConstants.NAMESPACE_TYPE_RELEVANCE, cContext)); |
| } |
| |
| private Proposal createProposal(String repString, String dispString, CompletionType type, int relevance, RemoteContentAssistInvocationContext context) { |
| return createProposal(repString, dispString, null, type, relevance, context); |
| } |
| |
| private Proposal createProposal(String repString, String dispString, String idString, CompletionType type, int relevance, RemoteContentAssistInvocationContext context) { |
| int parseOffset = context.getParseOffset(); |
| int invocationOffset = context.getInvocationOffset(); |
| boolean doReplacement = !context.isContextInformationStyle(); |
| |
| int repLength = doReplacement ? context.getCompletionNode().getLength() : 0; |
| int repOffset = doReplacement ? parseOffset - repLength : invocationOffset; |
| repString = doReplacement ? repString : ""; //$NON-NLS-1$ |
| |
| return new Proposal(repString, repOffset, repLength, type, dispString, idString, relevance); |
| } |
| |
| private CompletionType getElementType(IBinding binding) { |
| try { |
| if (binding instanceof ITypedef) { |
| return new CompletionType(ICElement.C_TYPEDEF); |
| } else if (binding instanceof ICompositeType) { |
| if (((ICompositeType)binding).getKey() == ICPPClassType.k_class || binding instanceof ICPPClassTemplate) |
| return new CompletionType(ICElement.C_CLASS); |
| else if (((ICompositeType)binding).getKey() == ICompositeType.k_struct) |
| return new CompletionType(ICElement.C_STRUCT); |
| else if (((ICompositeType)binding).getKey() == ICompositeType.k_union) |
| return new CompletionType(ICElement.C_UNION); |
| } else if (binding instanceof ICPPMethod) { |
| switch (((ICPPMethod)binding).getVisibility()) { |
| case ICPPMember.v_private: |
| return new CompletionType(ICElement.C_METHOD, Visibility.Private); |
| case ICPPMember.v_protected: |
| return new CompletionType(ICElement.C_METHOD, Visibility.Protected); |
| default: |
| return new CompletionType(ICElement.C_METHOD, Visibility.Public); |
| } |
| } else if (binding instanceof IFunction) { |
| return new CompletionType(ICElement.C_FUNCTION); |
| } else if (binding instanceof ICPPField) { |
| switch (((ICPPField)binding).getVisibility()) { |
| case ICPPMember.v_private: |
| return new CompletionType(ICElement.C_FIELD, Visibility.Private); |
| case ICPPMember.v_protected: |
| return new CompletionType(ICElement.C_FIELD, Visibility.Protected); |
| default: |
| return new CompletionType(ICElement.C_FIELD, Visibility.Public); |
| } |
| } else if (binding instanceof IField) { |
| return new CompletionType(ICElement.C_FIELD, Visibility.Public); |
| } else if (binding instanceof IVariable) { |
| return new CompletionType(ICElement.C_VARIABLE); |
| } else if (binding instanceof IEnumeration) { |
| return new CompletionType(ICElement.C_ENUMERATION); |
| } else if (binding instanceof IEnumerator) { |
| return new CompletionType(ICElement.C_ENUMERATOR); |
| } else if (binding instanceof ICPPNamespace) { |
| return new CompletionType(ICElement.C_NAMESPACE); |
| } else if (binding instanceof ICPPFunctionTemplate) { |
| return new CompletionType(ICElement.C_FUNCTION); |
| } else if (binding instanceof ICPPUsingDeclaration) { |
| IBinding[] delegates = ((ICPPUsingDeclaration)binding).getDelegates(); |
| if (delegates.length > 0) |
| return getElementType(delegates[0]); |
| } |
| } catch (DOMException e) { |
| } |
| return null; |
| } |
| |
| /** |
| * Compute base relevance depending on quality of name / prefix match. |
| * |
| * @param prefix the completion prefix |
| * @param match the matching identifier |
| * @return a relevance value indicating the quality of the name match |
| */ |
| protected int computeBaseRelevance(String prefix, String match) { |
| int baseRelevance= RelevanceConstants.DEFAULT_TYPE_RELEVANCE; |
| boolean caseMatch= prefix.length() > 0 && match.startsWith(prefix); |
| if (caseMatch) { |
| baseRelevance += RelevanceConstants.CASE_MATCH_RELEVANCE; |
| } |
| boolean exactNameMatch= match.equalsIgnoreCase(prefix); |
| if (exactNameMatch) { |
| baseRelevance += RelevanceConstants.EXACT_NAME_MATCH_RELEVANCE; |
| } |
| return baseRelevance; |
| } |
| } |