Introduce priorities into the code-completion results.

llvm-svn: 104751
This commit is contained in:
Douglas Gregor
2010-05-26 22:00:08 +00:00
parent e58f7b3c76
commit a2db793ff0
10 changed files with 202 additions and 69 deletions

View File

@@ -1794,6 +1794,21 @@ clang_getCompletionChunkCompletionString(CXCompletionString completion_string,
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.
*

View File

@@ -24,6 +24,41 @@ class raw_ostream;
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;
@@ -156,13 +191,14 @@ private:
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");
@@ -232,8 +268,9 @@ public:
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,
@@ -284,8 +321,11 @@ public:
/// \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;
@@ -313,29 +353,30 @@ public:
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) { }
@@ -356,6 +397,9 @@ public:
CodeCompletionString *CreateCodeCompletionString(Sema &S);
void Destroy();
/// brief Determine a base priority for the given declaration.
static unsigned getPriorityFromDecl(NamedDecl *ND);
};
class OverloadCandidate {

View File

@@ -210,9 +210,10 @@ CodeCompletionString::Chunk::Destroy() {
}
}
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 {
@@ -310,15 +311,13 @@ void CodeCompletionString::Serialize(llvm::raw_ostream &OS) 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)
@@ -327,7 +326,7 @@ CodeCompletionString *CodeCompletionString::Deserialize(const char *&Str,
// Parse the next kind.
unsigned KindValue;
if (ReadUnsigned(Str, StrEnd, KindValue))
return Result;
return false;
switch (ChunkKind Kind = (ChunkKind)KindValue) {
case CK_TypedText:
@@ -338,16 +337,17 @@ CodeCompletionString *CodeCompletionString::Deserialize(const char *&Str,
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;
}
@@ -365,12 +365,12 @@ CodeCompletionString *CodeCompletionString::Deserialize(const char *&Str,
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() {
@@ -380,6 +380,25 @@ 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
//===----------------------------------------------------------------------===//
@@ -584,6 +603,7 @@ CIndexCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &SemaRef,
}
WriteUnsigned(OS, Kind);
WriteUnsigned(OS, Results[I].Priority);
CodeCompletionString *CCS = Results[I].CreateCodeCompletionString(SemaRef);
assert(CCS && "No code-completion string?");
CCS->Serialize(OS);
@@ -598,6 +618,7 @@ CIndexCodeCompleteConsumer::ProcessOverloadCandidates(Sema &SemaRef,
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?");

View File

@@ -539,8 +539,10 @@ void ResultBuilder::MaybeAddResult(Result R, DeclContext *CurContext) {
// 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 &&
@@ -588,8 +590,10 @@ void ResultBuilder::AddResult(Result R, DeclContext *CurContext,
// 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()))
@@ -608,6 +612,10 @@ void ResultBuilder::AddResult(Result R, DeclContext *CurContext,
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);
}
@@ -751,34 +759,34 @@ namespace {
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
@@ -790,10 +798,17 @@ static void AddTypeSpecifierResults(const LangOptions &LangOpts,
}
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));
}
}
}
@@ -1299,6 +1314,8 @@ static void AddOrdinaryNameResults(Action::CodeCompletionContext CCC,
Pattern->AddPlaceholderChunk("expression");
Results.AddResult(Result(Pattern));
}
// FIXME: Rethrow?
}
if (SemaRef.getLangOptions().ObjC1) {

View File

@@ -8,8 +8,11 @@ int test(int i, int j, int k, int l) {
}
// 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

View File

@@ -26,5 +26,5 @@ void test_props(Int* ptr) {
// 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)

View File

@@ -782,7 +782,8 @@ void print_completion_result(CXCompletionResult *completion_result,
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) {

View File

@@ -37,12 +37,27 @@
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;
@@ -97,7 +112,8 @@ clang_getCompletionChunkKind(CXCompletionString completion_string,
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);
@@ -140,7 +156,8 @@ CXString clang_getCompletionChunkText(CXCompletionString completion_string,
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;
@@ -177,10 +194,17 @@ clang_getCompletionChunkCompletionString(CXCompletionString completion_string,
}
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)
@@ -226,7 +250,7 @@ AllocatedCXCodeCompleteResults::AllocatedCXCodeCompleteResults()
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;
@@ -376,10 +400,16 @@ CXCodeCompleteResults *clang_codeComplete(CXIndex CIdx,
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.

View File

@@ -30,6 +30,7 @@ _clang_getClangVersion
_clang_getCompletionChunkCompletionString
_clang_getCompletionChunkKind
_clang_getCompletionChunkText
_clang_getCompletionPriority
_clang_getCursor
_clang_getCursorDefinition
_clang_getCursorExtent

View File

@@ -30,6 +30,7 @@ clang_getClangVersion
clang_getCompletionChunkCompletionString
clang_getCompletionChunkKind
clang_getCompletionChunkText
clang_getCompletionPriority
clang_getCursor
clang_getCursorDefinition
clang_getCursorExtent