CINDEX_LINKAGE unsigned
clang_getNumCompletionChunks(CXCompletionString completion_string);
+/**
+ * \brief Determine the priority of this code completion.
+ *
+ * The priority of a code completion indicates how likely it is that this
+ * particular completion is the completion that the user will select. The
+ * priority is selected by various internal heuristics.
+ *
+ * \param completion_string The completion string to query.
+ *
+ * \returns The priority of this completion string. Smaller values indicate
+ * higher-priority (more likely) completions.
+ */
+CINDEX_LINKAGE unsigned
+clang_getCompletionPriority(CXCompletionString completion_string);
+
/**
* \brief Contains the results of code-completion.
*
namespace clang {
+/// \brief Default priority values for code-completion results based
+/// on their kind.
+enum {
+ /// \brief Priority for a declaration that is in the local scope.
+ CCP_LocalDeclaration = 8,
+ /// \brief Priority for a member declaration found from the current
+ /// method or member function.
+ CCP_MemberDeclaration = 20,
+ /// \brief Priority for a language keyword (that isn't any of the other
+ /// categories).
+ CCP_Keyword = 30,
+ /// \brief Priority for a code pattern.
+ CCP_CodePattern = 30,
+ /// \brief Priority for a type.
+ CCP_Type = 40,
+ /// \brief Priority for a non-type declaration.
+ CCP_Declaration = 50,
+ /// \brief Priority for a constant value (e.g., enumerator).
+ CCP_Constant = 60,
+ /// \brief Priority for a preprocessor macro.
+ CCP_Macro = 70,
+ /// \brief Priority for a nested-name-specifier.
+ CCP_NestedNameSpecifier = 75,
+ /// \brief Priority for a result that isn't likely to be what the user wants,
+ /// but is included for completeness.
+ CCP_Unlikely = 80
+};
+
+/// \brief Priority value deltas that are applied to code-completion results
+/// based on the context of the result.
+enum {
+ /// \brief The result is in a base class.
+ CCD_InBaseClass = 2
+};
+
class FunctionDecl;
class FunctionType;
class FunctionTemplateDecl;
public:
CodeCompletionString() { }
- ~CodeCompletionString();
+ ~CodeCompletionString() { clear(); }
typedef llvm::SmallVector<Chunk, 4>::const_iterator iterator;
iterator begin() const { return Chunks.begin(); }
iterator end() const { return Chunks.end(); }
bool empty() const { return Chunks.empty(); }
unsigned size() const { return Chunks.size(); }
+ void clear();
Chunk &operator[](unsigned I) {
assert(I < size() && "Chunk index out-of-range");
void Serialize(llvm::raw_ostream &OS) const;
/// \brief Deserialize a code-completion string from the given string.
- static CodeCompletionString *Deserialize(const char *&Str,
- const char *StrEnd);
+ ///
+ /// \returns true if successful, false otherwise.
+ bool Deserialize(const char *&Str, const char *StrEnd);
};
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
/// \brief When Kind == RK_Macro, the identifier that refers to a macro.
IdentifierInfo *Macro;
};
-
- /// \brief Specifiers which parameter (of a function, Objective-C method,
+
+ /// \brief The priority of this particular code-completion result.
+ unsigned Priority;
+
+ /// \brief Specifies which parameter (of a function, Objective-C method,
/// macro, etc.) we should start with when formatting the result.
unsigned StartParameter;
NestedNameSpecifier *Qualifier = 0,
bool QualifierIsInformative = false)
: Kind(RK_Declaration), Declaration(Declaration),
- StartParameter(0), Hidden(false),
- QualifierIsInformative(QualifierIsInformative),
+ Priority(getPriorityFromDecl(Declaration)), StartParameter(0),
+ Hidden(false), QualifierIsInformative(QualifierIsInformative),
StartsNestedNameSpecifier(false), AllParametersAreInformative(false),
- Qualifier(Qualifier) { }
+ Qualifier(Qualifier) {
+ }
/// \brief Build a result that refers to a keyword or symbol.
- Result(const char *Keyword)
- : Kind(RK_Keyword), Keyword(Keyword), StartParameter(0),
- Hidden(false), QualifierIsInformative(0),
+ Result(const char *Keyword, unsigned Priority = CCP_Keyword)
+ : Kind(RK_Keyword), Keyword(Keyword), Priority(Priority),
+ StartParameter(0), Hidden(false), QualifierIsInformative(0),
StartsNestedNameSpecifier(false), AllParametersAreInformative(false),
Qualifier(0) { }
/// \brief Build a result that refers to a macro.
- Result(IdentifierInfo *Macro)
- : Kind(RK_Macro), Macro(Macro), StartParameter(0),
+ Result(IdentifierInfo *Macro, unsigned Priority = CCP_Macro)
+ : Kind(RK_Macro), Macro(Macro), Priority(Priority), StartParameter(0),
Hidden(false), QualifierIsInformative(0),
StartsNestedNameSpecifier(false), AllParametersAreInformative(false),
Qualifier(0) { }
/// \brief Build a result that refers to a pattern.
- Result(CodeCompletionString *Pattern)
- : Kind(RK_Pattern), Pattern(Pattern), StartParameter(0),
- Hidden(false), QualifierIsInformative(0),
+ Result(CodeCompletionString *Pattern, unsigned Priority = CCP_CodePattern)
+ : Kind(RK_Pattern), Pattern(Pattern), Priority(Priority),
+ StartParameter(0), Hidden(false), QualifierIsInformative(0),
StartsNestedNameSpecifier(false), AllParametersAreInformative(false),
Qualifier(0) { }
CodeCompletionString *CreateCodeCompletionString(Sema &S);
void Destroy();
+
+ /// brief Determine a base priority for the given declaration.
+ static unsigned getPriorityFromDecl(NamedDecl *ND);
};
class OverloadCandidate {
}
}
-CodeCompletionString::~CodeCompletionString() {
+void CodeCompletionString::clear() {
std::for_each(Chunks.begin(), Chunks.end(),
std::mem_fun_ref(&Chunk::Destroy));
+ Chunks.clear();
}
std::string CodeCompletionString::getAsString() const {
}
}
-CodeCompletionString *CodeCompletionString::Deserialize(const char *&Str,
- const char *StrEnd) {
+bool CodeCompletionString::Deserialize(const char *&Str, const char *StrEnd) {
if (Str == StrEnd || *Str == 0)
- return 0;
+ return false;
- CodeCompletionString *Result = new CodeCompletionString;
unsigned NumBlocks;
if (ReadUnsigned(Str, StrEnd, NumBlocks))
- return Result;
+ return false;
for (unsigned I = 0; I != NumBlocks; ++I) {
if (Str + 1 >= StrEnd)
// Parse the next kind.
unsigned KindValue;
if (ReadUnsigned(Str, StrEnd, KindValue))
- return Result;
+ return false;
switch (ChunkKind Kind = (ChunkKind)KindValue) {
case CK_TypedText:
case CK_CurrentParameter: {
unsigned StrLen;
if (ReadUnsigned(Str, StrEnd, StrLen) || (Str + StrLen > StrEnd))
- return Result;
+ return false;
- Result->AddChunk(Chunk(Kind, StringRef(Str, StrLen)));
+ AddChunk(Chunk(Kind, StringRef(Str, StrLen)));
Str += StrLen;
break;
}
case CK_Optional: {
- std::auto_ptr<CodeCompletionString> Optional(Deserialize(Str, StrEnd));
- Result->AddOptionalChunk(Optional);
+ std::auto_ptr<CodeCompletionString> Optional(new CodeCompletionString());
+ if (Optional->Deserialize(Str, StrEnd))
+ AddOptionalChunk(Optional);
break;
}
case CK_Equal:
case CK_HorizontalSpace:
case CK_VerticalSpace:
- Result->AddChunk(Chunk(Kind));
+ AddChunk(Chunk(Kind));
break;
}
};
- return Result;
+ return true;
}
void CodeCompleteConsumer::Result::Destroy() {
}
}
+unsigned CodeCompleteConsumer::Result::getPriorityFromDecl(NamedDecl *ND) {
+ if (!ND)
+ return CCP_Unlikely;
+
+ // Context-based decisions.
+ DeclContext *DC = ND->getDeclContext()->getLookupContext();
+ if (DC->isFunctionOrMethod() || isa<BlockDecl>(DC))
+ return CCP_LocalDeclaration;
+ if (DC->isRecord() || isa<ObjCContainerDecl>(DC))
+ return CCP_MemberDeclaration;
+
+ // Content-based decisions.
+ if (isa<EnumConstantDecl>(ND))
+ return CCP_Constant;
+ if (isa<TypeDecl>(ND) || isa<ObjCInterfaceDecl>(ND))
+ return CCP_Type;
+ return CCP_Declaration;
+}
+
//===----------------------------------------------------------------------===//
// Code completion overload candidate implementation
//===----------------------------------------------------------------------===//
}
WriteUnsigned(OS, Kind);
+ WriteUnsigned(OS, Results[I].Priority);
CodeCompletionString *CCS = Results[I].CreateCodeCompletionString(SemaRef);
assert(CCS && "No code-completion string?");
CCS->Serialize(OS);
unsigned NumCandidates) {
for (unsigned I = 0; I != NumCandidates; ++I) {
WriteUnsigned(OS, CXCursor_NotImplemented);
+ WriteUnsigned(OS, /*Priority=*/0);
CodeCompletionString *CCS
= Candidates[I].CreateSignatureString(CurrentArg, SemaRef);
assert(CCS && "No code-completion string?");
// If the filter is for nested-name-specifiers, then this result starts a
// nested-name-specifier.
- if (AsNestedNameSpecifier)
+ if (AsNestedNameSpecifier) {
R.StartsNestedNameSpecifier = true;
+ R.Priority = CCP_NestedNameSpecifier;
+ }
// If this result is supposed to have an informative qualifier, add one.
if (R.QualifierIsInformative && !R.Qualifier &&
// If the filter is for nested-name-specifiers, then this result starts a
// nested-name-specifier.
- if (AsNestedNameSpecifier)
+ if (AsNestedNameSpecifier) {
R.StartsNestedNameSpecifier = true;
+ R.Priority = CCP_NestedNameSpecifier;
+ }
else if (Filter == &ResultBuilder::IsMember && !R.Qualifier && InBaseClass &&
isa<CXXRecordDecl>(R.Declaration->getDeclContext()
->getLookupContext()))
R.QualifierIsInformative = false;
}
+ // Adjust the priority if this result comes from a base class.
+ if (InBaseClass)
+ R.Priority += CCD_InBaseClass;
+
// Insert this result into the set of results.
Results.push_back(R);
}
static void AddTypeSpecifierResults(const LangOptions &LangOpts,
ResultBuilder &Results) {
typedef CodeCompleteConsumer::Result Result;
- Results.AddResult(Result("short"));
- Results.AddResult(Result("long"));
- Results.AddResult(Result("signed"));
- Results.AddResult(Result("unsigned"));
- Results.AddResult(Result("void"));
- Results.AddResult(Result("char"));
- Results.AddResult(Result("int"));
- Results.AddResult(Result("float"));
- Results.AddResult(Result("double"));
- Results.AddResult(Result("enum"));
- Results.AddResult(Result("struct"));
- Results.AddResult(Result("union"));
- Results.AddResult(Result("const"));
- Results.AddResult(Result("volatile"));
+ Results.AddResult(Result("short", CCP_Type));
+ Results.AddResult(Result("long", CCP_Type));
+ Results.AddResult(Result("signed", CCP_Type));
+ Results.AddResult(Result("unsigned", CCP_Type));
+ Results.AddResult(Result("void", CCP_Type));
+ Results.AddResult(Result("char", CCP_Type));
+ Results.AddResult(Result("int", CCP_Type));
+ Results.AddResult(Result("float", CCP_Type));
+ Results.AddResult(Result("double", CCP_Type));
+ Results.AddResult(Result("enum", CCP_Type));
+ Results.AddResult(Result("struct", CCP_Type));
+ Results.AddResult(Result("union", CCP_Type));
+ Results.AddResult(Result("const", CCP_Type));
+ Results.AddResult(Result("volatile", CCP_Type));
if (LangOpts.C99) {
// C99-specific
- Results.AddResult(Result("_Complex"));
- Results.AddResult(Result("_Imaginary"));
- Results.AddResult(Result("_Bool"));
- Results.AddResult(Result("restrict"));
+ Results.AddResult(Result("_Complex", CCP_Type));
+ Results.AddResult(Result("_Imaginary", CCP_Type));
+ Results.AddResult(Result("_Bool", CCP_Type));
+ Results.AddResult(Result("restrict", CCP_Type));
}
if (LangOpts.CPlusPlus) {
// C++-specific
- Results.AddResult(Result("bool"));
- Results.AddResult(Result("class"));
- Results.AddResult(Result("wchar_t"));
+ Results.AddResult(Result("bool", CCP_Type));
+ Results.AddResult(Result("class", CCP_Type));
+ Results.AddResult(Result("wchar_t", CCP_Type));
if (Results.includeCodePatterns()) {
// typename qualified-id
}
if (LangOpts.CPlusPlus0x) {
- Results.AddResult(Result("auto"));
- Results.AddResult(Result("char16_t"));
- Results.AddResult(Result("char32_t"));
- Results.AddResult(Result("decltype"));
+ Results.AddResult(Result("auto", CCP_Type));
+ Results.AddResult(Result("char16_t", CCP_Type));
+ Results.AddResult(Result("char32_t", CCP_Type));
+ if (Results.includeCodePatterns()) {
+ CodeCompletionString *Pattern = new CodeCompletionString;
+ Pattern->AddTypedTextChunk("decltype");
+ Pattern->AddChunk(CodeCompletionString::CK_LeftParen);
+ Pattern->AddPlaceholderChunk("expression-or-type");
+ Pattern->AddChunk(CodeCompletionString::CK_RightParen);
+ Results.AddResult(Result(Pattern));
+ }
}
}
Pattern->AddPlaceholderChunk("expression");
Results.AddResult(Result(Pattern));
}
+
+ // FIXME: Rethrow?
}
if (SemaRef.getLangOptions().ObjC1) {
}
// RUN: c-index-test -code-completion-at=%s:7:9 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC1 %s
-// CHECK-CC1: FunctionDecl:{ResultType int}{TypedText f}{LeftParen (}{Placeholder int}{RightParen )}
-// CHECK-CC1: NotImplemented:{TypedText sizeof}{LeftParen (}{Placeholder expression-or-type}{RightParen )}
+// CHECK-CC1: macro definition:{TypedText __VERSION__} (70)
+// CHECK-CC1: FunctionDecl:{ResultType int}{TypedText f}{LeftParen (}{Placeholder int}{RightParen )} (50)
+// CHECK-CC1: NotImplemented:{TypedText float} (40)
+// CHECK-CC1: ParmDecl:{ResultType int}{TypedText j} (8)
+// CHECK-CC1: NotImplemented:{TypedText sizeof}{LeftParen (}{Placeholder expression-or-type}{RightParen )} (30)
// RUN: c-index-test -code-completion-at=%s:7:14 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC1 %s
// RUN: c-index-test -code-completion-at=%s:7:18 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC1 %s
// RUN: c-index-test -code-completion-at=%s:7:22 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC1 %s
// CHECK-CC1: ObjCPropertyDecl:{ResultType int}{TypedText prop1}
// CHECK-CC1: ObjCPropertyDecl:{ResultType float}{TypedText ProtoProp}
// RUN: c-index-test -code-completion-at=%s:22:8 %s | FileCheck -check-prefix=CHECK-CC2 %s
-// CHECK-CC2: ObjCIvarDecl:{ResultType int}{TypedText IVar}
-// CHECK-CC2: ObjCIvarDecl:{ResultType int}{TypedText SuperIVar}
+// CHECK-CC2: ObjCIvarDecl:{ResultType int}{TypedText IVar} (20)
+// CHECK-CC2: ObjCIvarDecl:{ResultType int}{TypedText SuperIVar} (22)
clang_disposeString(ks);
print_completion_string(completion_result->CompletionString, file);
- fprintf(file, "\n");
+ fprintf(file, " (%u)\n",
+ clang_getCompletionPriority(completion_result->CompletionString));
}
int perform_code_completion(int argc, const char **argv) {
using namespace clang;
using namespace clang::cxstring;
+namespace {
+ /// \brief Stored representation of a completion string.
+ ///
+ /// This is the representation behind a CXCompletionString.
+ class CXStoredCodeCompletionString : public CodeCompletionString {
+ unsigned Priority;
+
+ public:
+ CXStoredCodeCompletionString(unsigned Priority) : Priority(Priority) { }
+
+ unsigned getPriority() const { return Priority; }
+ };
+}
+
extern "C" {
enum CXCompletionChunkKind
clang_getCompletionChunkKind(CXCompletionString completion_string,
unsigned chunk_number) {
- CodeCompletionString *CCStr = (CodeCompletionString *)completion_string;
+ CXStoredCodeCompletionString *CCStr
+ = (CXStoredCodeCompletionString *)completion_string;
if (!CCStr || chunk_number >= CCStr->size())
return CXCompletionChunk_Text;
CXString clang_getCompletionChunkText(CXCompletionString completion_string,
unsigned chunk_number) {
- CodeCompletionString *CCStr = (CodeCompletionString *)completion_string;
+ CXStoredCodeCompletionString *CCStr
+ = (CXStoredCodeCompletionString *)completion_string;
if (!CCStr || chunk_number >= CCStr->size())
return createCXString(0);
CXCompletionString
clang_getCompletionChunkCompletionString(CXCompletionString completion_string,
unsigned chunk_number) {
- CodeCompletionString *CCStr = (CodeCompletionString *)completion_string;
+ CXStoredCodeCompletionString *CCStr
+ = (CXStoredCodeCompletionString *)completion_string;
if (!CCStr || chunk_number >= CCStr->size())
return 0;
}
unsigned clang_getNumCompletionChunks(CXCompletionString completion_string) {
- CodeCompletionString *CCStr = (CodeCompletionString *)completion_string;
+ CXStoredCodeCompletionString *CCStr
+ = (CXStoredCodeCompletionString *)completion_string;
return CCStr? CCStr->size() : 0;
}
+unsigned clang_getCompletionPriority(CXCompletionString completion_string) {
+ CXStoredCodeCompletionString *CCStr
+ = (CXStoredCodeCompletionString *)completion_string;
+ return CCStr? CCStr->getPriority() : CCP_Unlikely;
+}
+
static bool ReadUnsigned(const char *&Memory, const char *MemoryEnd,
unsigned &Value) {
if (Memory + sizeof(unsigned) > MemoryEnd)
AllocatedCXCodeCompleteResults::~AllocatedCXCodeCompleteResults() {
for (unsigned I = 0, N = NumResults; I != N; ++I)
- delete (CodeCompletionString *)Results[I].CompletionString;
+ delete (CXStoredCodeCompletionString *)Results[I].CompletionString;
delete [] Results;
delete Buffer;
if (ReadUnsigned(Str, StrEnd, KindValue))
break;
- CodeCompletionString *CCStr
- = CodeCompletionString::Deserialize(Str, StrEnd);
- if (!CCStr)
+ unsigned Priority;
+ if (ReadUnsigned(Str, StrEnd, Priority))
+ break;
+
+ CXStoredCodeCompletionString *CCStr
+ = new CXStoredCodeCompletionString(Priority);
+ if (!CCStr->Deserialize(Str, StrEnd)) {
+ delete CCStr;
continue;
+ }
if (!CCStr->empty()) {
// Vend the code-completion result to the caller.
_clang_getCompletionChunkCompletionString
_clang_getCompletionChunkKind
_clang_getCompletionChunkText
+_clang_getCompletionPriority
_clang_getCursor
_clang_getCursorDefinition
_clang_getCursorExtent
clang_getCompletionChunkCompletionString
clang_getCompletionChunkKind
clang_getCompletionChunkText
+clang_getCompletionPriority
clang_getCursor
clang_getCursorDefinition
clang_getCursorExtent