Introduce code completion strings, which describe how to *use* the

results of code completion, e.g., by providing function call syntax
with placeholders for each of the parameters.

llvm-svn: 82293
This commit is contained in:
Douglas Gregor 2009-09-18 22:15:54 +00:00
parent b90893dc41
commit fedc328ae9
3 changed files with 241 additions and 2 deletions

View File

@ -16,8 +16,11 @@
#include "clang/AST/DeclarationName.h"
#include "clang/AST/Type.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include <list>
#include <map>
#include <memory>
#include <string>
#include <vector>
namespace llvm {
@ -31,6 +34,103 @@ class DeclContext;
class NamedDecl;
class Scope;
class Sema;
/// \brief A "string" used to describe how code completion can
/// be performed for an entity.
///
/// A code completion string typically shows how a particular entity can be
/// used. For example, the code completion string for a function would show
/// the syntax to call it, including the parentheses, placeholders for the
/// arguments, etc.
class CodeCompletionString {
public:
/// \brief The different kinds of "chunks" that can occur within a code
/// completion string.
enum ChunkKind {
/// \brief A piece of text that should be placed in the buffer, e.g.,
/// parentheses or a comma in a function call.
CK_Text,
/// \brief A code completion string that is entirely optional. For example,
/// an optional code completion string that describes the default arguments
/// in a function call.
CK_Optional,
/// \brief A string that acts as a placeholder for, e.g., a function
/// call argument.
CK_Placeholder
};
/// \brief One piece of the code completion string.
struct Chunk {
/// \brief The kind of data stored in this piece of the code completion
/// string.
ChunkKind Kind;
union {
/// \brief The text string associated with a CK_Text chunk.
/// The string is owned by the chunk and will be deallocated
/// (with delete[]) when the chunk is destroyed.
const char *Text;
/// \brief The code completion string associated with a CK_Optional chunk.
/// The optional code completion string is owned by the chunk, and will
/// be deallocated (with delete) when the chunk is destroyed.
CodeCompletionString *Optional;
/// \brief Placeholder text associated with a CK_Placeholder chunk.
/// The string is owned by the chunk and will be deallocated (with
/// delete[]) when the chunk is destroyed.
const char *Placeholder;
};
/// \brief Create a new text chunk.
static Chunk CreateText(const char *Text);
/// \brief Create a new optional chunk.
static Chunk CreateOptional(std::auto_ptr<CodeCompletionString> Optional);
/// \brief Create a new placeholder chunk.
static Chunk CreatePlaceholder(const char *Placeholder);
/// \brief Destroy this chunk.
void Destroy();
};
private:
/// \brief The chunks stored in this string.
llvm::SmallVector<Chunk, 4> Chunks;
CodeCompletionString(const CodeCompletionString &); // DO NOT IMPLEMENT
CodeCompletionString &operator=(const CodeCompletionString &); // DITTO
public:
CodeCompletionString() { }
~CodeCompletionString();
typedef llvm::SmallVector<Chunk, 4>::const_iterator iterator;
iterator begin() const { return Chunks.begin(); }
iterator end() const { return Chunks.end(); }
/// \brief Add a new text chunk.
/// The text string will be copied.
void AddTextChunk(const char *Text) {
Chunks.push_back(Chunk::CreateText(Text));
}
/// \brief Add a new optional chunk.
void AddOptionalChunk(std::auto_ptr<CodeCompletionString> Optional) {
Chunks.push_back(Chunk::CreateOptional(Optional));
}
/// \brief Add a new placeholder chunk.
/// The placeholder text will be copied.
void AddPlaceholderChunk(const char *Placeholder) {
Chunks.push_back(Chunk::CreatePlaceholder(Placeholder));
}
/// \brief Retrieve a string representation of the code completion string,
/// which is mainly useful for debugging.
std::string getAsString() const;
};
/// \brief Abstract interface for a consumer of code-completion
/// information.
@ -264,6 +364,7 @@ public:
//@{
bool canHiddenResultBeFound(NamedDecl *Hidden, NamedDecl *Visible);
void AddTypeSpecifierResults(unsigned Rank, ResultSet &Results);
CodeCompletionString *CreateCodeCompletionString(Result R);
//@}
};

View File

@ -19,9 +19,75 @@
#include "llvm/Support/Compiler.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <string.h>
#include <cstring>
#include <functional>
using namespace clang;
//===----------------------------------------------------------------------===//
// Code completion string implementation
//===----------------------------------------------------------------------===//
CodeCompletionString::Chunk
CodeCompletionString::Chunk::CreateText(const char *Text) {
Chunk Result;
Result.Kind = CK_Text;
char *New = new char [std::strlen(Text) + 1];
std::strcpy(New, Text);
Result.Text = New;
return Result;
}
CodeCompletionString::Chunk
CodeCompletionString::Chunk::CreateOptional(
std::auto_ptr<CodeCompletionString> Optional) {
Chunk Result;
Result.Kind = CK_Optional;
Result.Optional = Optional.release();
return Result;
}
CodeCompletionString::Chunk
CodeCompletionString::Chunk::CreatePlaceholder(const char *Placeholder) {
Chunk Result;
Result.Kind = CK_Placeholder;
char *New = new char [std::strlen(Placeholder) + 1];
std::strcpy(New, Placeholder);
Result.Placeholder = New;
return Result;
}
void
CodeCompletionString::Chunk::Destroy() {
switch (Kind) {
case CK_Text: delete [] Text; break;
case CK_Optional: delete Optional; break;
case CK_Placeholder: delete [] Placeholder; break;
}
}
CodeCompletionString::~CodeCompletionString() {
std::for_each(Chunks.begin(), Chunks.end(),
std::mem_fun_ref(&Chunk::Destroy));
}
std::string CodeCompletionString::getAsString() const {
std::string Result;
llvm::raw_string_ostream OS(Result);
for (iterator C = begin(), CEnd = end(); C != CEnd; ++C) {
switch (C->Kind) {
case CK_Text: OS << C->Text; break;
case CK_Optional: OS << "{#" << C->Optional->getAsString() << "#}"; break;
case CK_Placeholder: OS << "<#" << C->Placeholder << "#>"; break;
}
}
return Result;
}
//===----------------------------------------------------------------------===//
// Code completion consumer implementation
//===----------------------------------------------------------------------===//
CodeCompleteConsumer::CodeCompleteConsumer(Sema &S) : SemaRef(S) {
SemaRef.setCodeCompleteConsumer(this);
}
@ -180,7 +246,7 @@ void CodeCompleteConsumer::CodeCompleteOperatorName(Scope *S) {
// Add the names of overloadable operators.
#define OVERLOADED_OPERATOR(Name,Spelling,Token,Unary,Binary,MemberOnly) \
if (strcmp(Spelling, "?")) \
if (std::strcmp(Spelling, "?")) \
Results.MaybeAddResult(Result(Spelling, 0));
#include "clang/Basic/OperatorKinds.def"
@ -663,6 +729,64 @@ void CodeCompleteConsumer::AddTypeSpecifierResults(unsigned Rank,
}
}
/// \brief Add function parameter chunks to the given code completion string.
static void AddFunctionParameterChunks(ASTContext &Context,
FunctionDecl *Function,
CodeCompletionString *Result) {
CodeCompletionString *CCStr = Result;
for (unsigned P = 0, N = Function->getNumParams(); P != N; ++P) {
ParmVarDecl *Param = Function->getParamDecl(P);
if (Param->hasDefaultArg()) {
// When we see an optional default argument, put that argument and
// the remaining default arguments into a new, optional string.
CodeCompletionString *Opt = new CodeCompletionString;
CCStr->AddOptionalChunk(std::auto_ptr<CodeCompletionString>(Opt));
CCStr = Opt;
}
if (P != 0)
CCStr->AddTextChunk(", ");
// Format the placeholder string.
std::string PlaceholderStr;
if (Param->getIdentifier())
PlaceholderStr = Param->getIdentifier()->getName();
Param->getType().getAsStringInternal(PlaceholderStr,
Context.PrintingPolicy);
// Add the placeholder string.
CCStr->AddPlaceholderChunk(PlaceholderStr.c_str());
}
}
/// \brief If possible, create a new code completion string for the given
/// result.
///
/// \returns Either a new, heap-allocated code completion string describing
/// how to use this result, or NULL to indicate that the string or name of the
/// result is all that is needed.
CodeCompletionString *
CodeCompleteConsumer::CreateCodeCompletionString(Result R) {
if (R.Kind != Result::RK_Declaration)
return 0;
NamedDecl *ND = R.Declaration;
if (FunctionDecl *Function = dyn_cast<FunctionDecl>(ND)) {
CodeCompletionString *Result = new CodeCompletionString;
Result->AddTextChunk(Function->getNameAsString().c_str());
Result->AddTextChunk("(");
AddFunctionParameterChunks(getSema().Context, Function, Result);
Result->AddTextChunk(")");
return Result;
}
return 0;
}
void
PrintingCodeCompleteConsumer::ProcessCodeCompleteResults(Result *Results,
unsigned NumResults) {
@ -677,6 +801,11 @@ PrintingCodeCompleteConsumer::ProcessCodeCompleteResults(Result *Results,
<< Results[I].Rank;
if (Results[I].Hidden)
OS << " (Hidden)";
if (CodeCompletionString *CCS = CreateCodeCompletionString(Results[I])) {
OS << " : " << CCS->getAsString();
delete CCS;
}
OS << '\n';
break;

View File

@ -0,0 +1,9 @@
// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
// RUN: true
void f(int i, int j = 2, int k = 5);
void f(float x, float y);
void test() {
// CHECK-CC1: f(<#int i#>{#, <#int j#>{#, <#int k#>#}#})
// CHECK-CC1: f(<#float x#>, <#float y#>)
::