Various improvements to Clang's code-completion infrastructure:

- Introduce more code-completion string "chunk" kinds that describe
  symbols, the actual text that the user is expected to type, etc.
  - Make the generation of macro results optional, since it can be
  slow
  - Make code-completion accessible through the C API, marshalling the
  code-completion results through a temporary file (ick) to maintain
  process separation.

The last doesn't have tests yet.

llvm-svn: 86306
This commit is contained in:
Douglas Gregor 2009-11-07 00:00:49 +00:00
parent ba78dcd90d
commit 9eb7701dff
9 changed files with 1424 additions and 65 deletions

View File

@ -305,6 +305,327 @@ CINDEX_LINKAGE void clang_getDefinitionSpellingAndExtent(CXCursor,
*/
CINDEX_LINKAGE CXDecl clang_getCursorDecl(CXCursor);
/**
* \brief A semantic string that describes a code-completion result.
*
* A semantic string that describes the formatting of a code-completion
* result as a single "template" of text that should be inserted into the
* source buffer when a particular code-completion result is selected.
* Each semantic string is made up of some number of "chunks", each of which
* contains some text along with a description of what that text means, e.g.,
* the name of the entity being referenced, whether the text chunk is part of
* the template, or whether it is a "placeholder" that the user should replace
* with actual code,of a specific kind. See \c CXCompletionChunkKind for a
* description of the different kinds of chunks.
*/
typedef void *CXCompletionString;
/**
* \brief A single result of code completion.
*/
typedef struct {
/**
* \brief The kind of entity that this completion refers to.
*
* The cursor kind will be a macro, keyword, or a declaration (one of the
* *Decl cursor kinds), describing the entity that the completion is
* referring to.
*
* \todo In the future, we would like to provide a full cursor, to allow
* the client to extract additional information from declaration.
*/
enum CXCursorKind CursorKind;
/**
* \brief The code-completion string that describes how to insert this
* code-completion result into the editing buffer.
*/
CXCompletionString CompletionString;
} CXCompletionResult;
/**
* \brief Describes a single piece of text within a code-completion string.
*
* Each "chunk" within a code-completion string (\c CXCompletionString) is
* either a piece of text with a specific "kind" that describes how that text
* should be interpreted by the client or is another completion string.
*/
enum CXCompletionChunkKind {
/**
* \brief A code-completion string that describes "optional" text that
* could be a part of the template (but is not required).
*
* The Optional chunk is the only kind of chunk that has a code-completion
* string for its representation, which is accessible via
* \c clang_getCompletionChunkCompletionString(). The code-completion string
* describes an additional part of the template that is completely optional.
* For example, optional chunks can be used to describe the placeholders for
* arguments that match up with defaulted function parameters, e.g. given:
*
* \code
* void f(int x, float y = 3.14, double z = 2.71828);
* \endcode
*
* The code-completion string for this function would contain:
* - a TypedText chunk for "f".
* - a LeftParen chunk for "(".
* - a Placeholder chunk for "int x"
* - an Optional chunk containing the remaining defaulted arguments, e.g.,
* - a Comma chunk for ","
* - a Placeholder chunk for "float x"
* - an Optional chunk containing the last defaulted argument:
* - a Comma chunk for ","
* - a Placeholder chunk for "double z"
* - a RightParen chunk for ")"
*
* There are many ways two handle Optional chunks. Two simple approaches are:
* - Completely ignore optional chunks, in which case the template for the
* function "f" would only include the first parameter ("int x").
* - Fully expand all optional chunks, in which case the template for the
* function "f" would have all of the parameters.
*/
CXCompletionChunk_Optional,
/**
* \brief Text that a user would be expected to type to get this
* code-completion result.
*
* There will be exactly one "typed text" chunk in a semantic string, which
* will typically provide the spelling of a keyword or the name of a
* declaration that could be used at the current code point. Clients are
* expected to filter the code-completion results based on the text in this
* chunk.
*/
CXCompletionChunk_TypedText,
/**
* \brief Text that should be inserted as part of a code-completion result.
*
* A "text" chunk represents text that is part of the template to be
* inserted into user code should this particular code-completion result
* be selected.
*/
CXCompletionChunk_Text,
/**
* \brief Placeholder text that should be replaced by the user.
*
* A "placeholder" chunk marks a place where the user should insert text
* into the code-completion template. For example, placeholders might mark
* the function parameters for a function declaration, to indicate that the
* user should provide arguments for each of those parameters. The actual
* text in a placeholder is a suggestion for the text to display before
* the user replaces the placeholder with real code.
*/
CXCompletionChunk_Placeholder,
/**
* \brief Informative text that should be displayed but never inserted as
* part of the template.
*
* An "informative" chunk contains annotations that can be displayed to
* help the user decide whether a particular code-completion result is the
* right option, but which is not part of the actual template to be inserted
* by code completion.
*/
CXCompletionChunk_Informative,
/**
* \brief Text that describes the current parameter when code-completion is
* referring to function call, message send, or template specialization.
*
* A "current parameter" chunk occurs when code-completion is providing
* information about a parameter corresponding to the argument at the
* code-completion point. For example, given a function
*
* \code
* int add(int x, int y);
* \endcode
*
* and the source code \c add(, where the code-completion point is after the
* "(", the code-completion string will contain a "current parameter" chunk
* for "int x", indicating that the current argument will initialize that
* parameter. After typing further, to \c add(17, (where the code-completion
* point is after the ","), the code-completion string will contain a
* "current paremeter" chunk to "int y".
*/
CXCompletionChunk_CurrentParameter,
/**
* \brief A left parenthesis ('('), used to initiate a function call or
* signal the beginning of a function parameter list.
*/
CXCompletionChunk_LeftParen,
/**
* \brief A right parenthesis (')'), used to finish a function call or
* signal the end of a function parameter list.
*/
CXCompletionChunk_RightParen,
/**
* \brief A left bracket ('[').
*/
CXCompletionChunk_LeftBracket,
/**
* \brief A right bracket (']').
*/
CXCompletionChunk_RightBracket,
/**
* \brief A left brace ('{').
*/
CXCompletionChunk_LeftBrace,
/**
* \brief A right brace ('}').
*/
CXCompletionChunk_RightBrace,
/**
* \brief A left angle bracket ('<').
*/
CXCompletionChunk_LeftAngle,
/**
* \brief A right angle bracket ('>').
*/
CXCompletionChunk_RightAngle,
/**
* \brief A comma separator (',').
*/
CXCompletionChunk_Comma
};
/**
* \brief Callback function that receives a single code-completion result.
*
* This callback will be invoked by \c clang_codeComplete() for each
* code-completion result.
*
* \param completion_result a pointer to the current code-completion result,
* providing one possible completion. The pointer itself is only valid
* during the execution of the completion callback.
*
* \param client_data the client data provided to \c clang_codeComplete().
*/
typedef void (*CXCompletionIterator)(CXCompletionResult *completion_result,
CXClientData client_data);
/**
* \brief Determine the kind of a particular chunk within a completion string.
*
* \param completion_string the completion string to query.
*
* \param chunk_number the 0-based index of the chunk in the completion string.
*
* \returns the kind of the chunk at the index \c chunk_number.
*/
CINDEX_LINKAGE enum CXCompletionChunkKind
clang_getCompletionChunkKind(CXCompletionString completion_string,
unsigned chunk_number);
/**
* \brief Retrieve the text associated with a particular chunk within a
* completion string.
*
* \param completion_string the completion string to query.
*
* \param chunk_number the 0-based index of the chunk in the completion string.
*
* \returns the text associated with the chunk at index \c chunk_number.
*/
CINDEX_LINKAGE const char *
clang_getCompletionChunkText(CXCompletionString completion_string,
unsigned chunk_number);
/**
* \brief Retrieve the completion string associated with a particular chunk
* within a completion string.
*
* \param completion_string the completion string to query.
*
* \param chunk_number the 0-based index of the chunk in the completion string.
*
* \returns the completion string associated with the chunk at index
* \c chunk_number, or NULL if that chunk is not represented by a completion
* string.
*/
CINDEX_LINKAGE CXCompletionString
clang_getCompletionChunkCompletionString(CXCompletionString completion_string,
unsigned chunk_number);
/**
* \brief Retrieve the number of chunks in the given code-completion string.
*/
CINDEX_LINKAGE unsigned
clang_getNumCompletionChunks(CXCompletionString completion_string);
/**
* \brief Perform code completion at a given location in a source file.
*
* This function performs code completion at a particular file, line, and
* column within source code, providing results that suggest potential
* code snippets based on the context of the completion. The basic model
* for code completion is that Clang will parse a complete source file,
* performing syntax checking up to the location where code-completion has
* been requested. At that point, a special code-completion token is passed
* to the parser, which recognizes this token and determines, based on the
* current location in the C/Objective-C/C++ grammar and the state of
* semantic analysis, what completions to provide. These completions are
* enumerated through a callback interface to the client.
*
* Code completion itself is meant to be triggered by the client when the
* user types punctuation characters or whitespace, at which point the
* code-completion location will coincide with the cursor. For example, if \c p
* is a pointer, code-completion might be triggered after the "-" and then
* after the ">" in \c p->. When the code-completion location is afer the ">",
* the completion results will provide, e.g., the members of the struct that
* "p" points to. The client is responsible for placing the cursor at the
* beginning of the token currently being typed, then filtering the results
* based on the contents of the token. For example, when code-completing for
* the expression \c p->get, the client should provide the location just after
* the ">" (e.g., pointing at the "g") to this code-completion hook. Then, the
* client can filter the results based on the current token text ("get"), only
* showing those results that start with "get". The intent of this interface
* is to separate the relatively high-latency acquisition of code-competion
* results from the filtering of results on a per-character basis, which must
* have a lower latency.
*
* \param CIdx the \c CXIndex instance that will be used to perform code
* completion.
*
* \param source_filename the name of the source file that should be parsed
* to perform code-completion. This source file must be the same as or
* include the filename described by \p complete_filename, or no code-completion
* results will be produced.
*
* \param num_command_line_args the number of command-line arguments stored in
* \p command_line_args.
*
* \param command_line_args the command-line arguments to pass to the Clang
* compiler to build the given source file. This should include all of the
* necessary include paths, language-dialect switches, precompiled header
* includes, etc., but should not include any information specific to
* code completion.
*
* \param complete_filename the name of the source file where code completion
* should be performed. In many cases, this name will be the same as the
* source filename. However, the completion filename may also be a file
* included by the source file, which is required when producing
* code-completion results for a header.
*
* \param complete_line the line at which code-completion should occur.
*
* \param complete_column the column at which code-completion should occur.
* Note that the column should point just after the syntactic construct that
* initiated code completion, and not in the middle of a lexical token.
*
* \param completion_iterator a callback function that will receive
* code-completion results.
*
* \param client_data client-specific data that will be passed back via the
* code-completion callback function.
*/
CINDEX_LINKAGE void clang_codeComplete(CXIndex CIdx,
const char *source_filename,
int num_command_line_args,
const char **command_line_args,
const char *complete_filename,
unsigned complete_line,
unsigned complete_column,
CXCompletionIterator completion_iterator,
CXClientData client_data);
#ifdef __cplusplus
}
#endif

View File

@ -14,6 +14,7 @@
#define LLVM_CLANG_SEMA_CODECOMPLETECONSUMER_H
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include <memory>
#include <string>
@ -43,6 +44,10 @@ public:
/// \brief The different kinds of "chunks" that can occur within a code
/// completion string.
enum ChunkKind {
/// \brief The piece of text that the user is expected to type to
/// match the code-completion string, typically a keyword or the name of a
/// declarator or macro.
CK_TypedText,
/// \brief A piece of text that should be placed in the buffer, e.g.,
/// parentheses or a comma in a function call.
CK_Text,
@ -55,7 +60,29 @@ public:
CK_Placeholder,
/// \brief A piece of text that describes something about the result but
/// should not be inserted into the buffer.
CK_Informative
CK_Informative,
/// \brief A piece of text that describes the parameter that corresponds
/// to the code-completion location within a function call, message send,
/// macro invocation, etc.
CK_CurrentParameter,
/// \brief A left parenthesis ('(').
CK_LeftParen,
/// \brief A right parenthesis (')').
CK_RightParen,
/// \brief A left bracket ('[').
CK_LeftBracket,
/// \brief A right bracket (']').
CK_RightBracket,
/// \brief A left brace ('{').
CK_LeftBrace,
/// \brief A right brace ('}').
CK_RightBrace,
/// \brief A left angle bracket ('<').
CK_LeftAngle,
/// \brief A right angle bracket ('>').
CK_RightAngle,
/// \brief A comma separator (',').
CK_Comma
};
/// \brief One piece of the code completion string.
@ -66,7 +93,7 @@ public:
union {
/// \brief The text string associated with a CK_Text, CK_Placeholder,
/// or CK_Informative chunk.
/// CK_Informative, or CK_Comma chunk.
/// The string is owned by the chunk and will be deallocated
/// (with delete[]) when the chunk is destroyed.
const char *Text;
@ -79,10 +106,8 @@ public:
Chunk() : Kind(CK_Text), Text(0) { }
private:
Chunk(ChunkKind Kind, const char *Text);
public:
Chunk(ChunkKind Kind, llvm::StringRef Text = 0);
/// \brief Create a new text chunk.
static Chunk CreateText(const char *Text);
@ -95,6 +120,9 @@ public:
/// \brief Create a new informative chunk.
static Chunk CreateInformative(const char *Informative);
/// \brief Create a new current-parameter chunk.
static Chunk CreateCurrentParameter(const char *CurrentParameter);
/// \brief Destroy this chunk, deallocating any memory it owns.
void Destroy();
};
@ -113,6 +141,24 @@ public:
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(); }
Chunk &operator[](unsigned I) {
assert(I < size() && "Chunk index out-of-range");
return Chunks[I];
}
const Chunk &operator[](unsigned I) const {
assert(I < size() && "Chunk index out-of-range");
return Chunks[I];
}
/// \brief Add a new typed-text chunk.
/// The text string will be copied.
void AddTypedTextChunk(const char *Text) {
Chunks.push_back(Chunk(CK_TypedText, Text));
}
/// \brief Add a new text chunk.
/// The text string will be copied.
@ -136,15 +182,37 @@ public:
void AddInformativeChunk(const char *Text) {
Chunks.push_back(Chunk::CreateInformative(Text));
}
/// \brief Add a new current-parameter chunk.
/// The text will be copied.
void AddCurrentParameterChunk(const char *CurrentParameter) {
Chunks.push_back(Chunk::CreateCurrentParameter(CurrentParameter));
}
/// \brief Add a new chunk.
void AddChunk(Chunk C) { Chunks.push_back(C); }
/// \brief Retrieve a string representation of the code completion string,
/// which is mainly useful for debugging.
std::string getAsString() const;
std::string getAsString() const;
/// \brief Serialize this code-completion string to the given stream.
void Serialize(llvm::raw_ostream &OS) const;
/// \brief Deserialize a code-completion string from the given string.
static CodeCompletionString *Deserialize(llvm::StringRef &Str);
};
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
const CodeCompletionString &CCS);
/// \brief Abstract interface for a consumer of code-completion
/// information.
class CodeCompleteConsumer {
protected:
/// \brief Whether to include macros in the code-completion results.
bool IncludeMacros;
public:
/// \brief Captures a result of code completion.
struct Result {
@ -291,6 +359,14 @@ public:
Sema &S) const;
};
CodeCompleteConsumer() : IncludeMacros(false) { }
explicit CodeCompleteConsumer(bool IncludeMacros)
: IncludeMacros(IncludeMacros) { }
/// \brief Whether the code-completion consumer wants to see macros.
bool includeMacros() const { return IncludeMacros; }
/// \brief Deregisters and destroys this code-completion consumer.
virtual ~CodeCompleteConsumer();
@ -326,8 +402,35 @@ class PrintingCodeCompleteConsumer : public CodeCompleteConsumer {
public:
/// \brief Create a new printing code-completion consumer that prints its
/// results to the given raw output stream.
PrintingCodeCompleteConsumer(Sema &S, llvm::raw_ostream &OS)
: SemaRef(S), OS(OS) { }
PrintingCodeCompleteConsumer(Sema &S, bool IncludeMacros,
llvm::raw_ostream &OS)
: CodeCompleteConsumer(IncludeMacros), SemaRef(S), OS(OS) { }
/// \brief Prints the finalized code-completion results.
virtual void ProcessCodeCompleteResults(Result *Results,
unsigned NumResults);
virtual void ProcessOverloadCandidates(unsigned CurrentArg,
OverloadCandidate *Candidates,
unsigned NumCandidates);
};
/// \brief A code-completion consumer that prints the results it receives
/// in a format that is parsable by the CIndex library.
class CIndexCodeCompleteConsumer : public CodeCompleteConsumer {
/// \brief The semantic-analysis object to which this code-completion
/// consumer is attached.
Sema &SemaRef;
/// \brief The raw output stream.
llvm::raw_ostream &OS;
public:
/// \brief Create a new CIndex code-completion consumer that prints its
/// results to the given raw output stream in a format readable to the CIndex
/// library.
CIndexCodeCompleteConsumer(Sema &S, bool IncludeMacros, llvm::raw_ostream &OS)
: CodeCompleteConsumer(IncludeMacros), SemaRef(S), OS(OS) { }
/// \brief Prints the finalized code-completion results.
virtual void ProcessCodeCompleteResults(Result *Results,

View File

@ -16,6 +16,7 @@
#include "clang/Lex/Preprocessor.h"
#include "Sema.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
@ -26,14 +27,62 @@ using namespace clang;
//===----------------------------------------------------------------------===//
// Code completion string implementation
//===----------------------------------------------------------------------===//
CodeCompletionString::Chunk::Chunk(ChunkKind Kind, const char *Text)
CodeCompletionString::Chunk::Chunk(ChunkKind Kind, llvm::StringRef Text)
: Kind(Kind), Text(0)
{
assert((Kind == CK_Text || Kind == CK_Placeholder || Kind == CK_Informative)
&& "Invalid text chunk kind");
char *New = new char [std::strlen(Text) + 1];
std::strcpy(New, Text);
this->Text = New;
switch (Kind) {
case CK_TypedText:
case CK_Text:
case CK_Placeholder:
case CK_Informative:
case CK_CurrentParameter: {
char *New = new char [Text.size() + 1];
std::memcpy(New, Text.data(), Text.size());
New[Text.size()] = '\0';
this->Text = New;
break;
}
case CK_Optional:
llvm::llvm_unreachable("Optional strings cannot be created from text");
break;
case CK_LeftParen:
this->Text = "(";
break;
case CK_RightParen:
this->Text = ")";
break;
case CK_LeftBracket:
this->Text = "[";
break;
case CK_RightBracket:
this->Text = "]";
break;
case CK_LeftBrace:
this->Text = "{";
break;
case CK_RightBrace:
this->Text = "}";
break;
case CK_LeftAngle:
this->Text = "<";
break;
case CK_RightAngle:
this->Text = ">";
break;
case CK_Comma:
this->Text = ", ";
break;
}
}
CodeCompletionString::Chunk
@ -60,6 +109,13 @@ CodeCompletionString::Chunk::CreateInformative(const char *Informative) {
return Chunk(CK_Informative, Informative);
}
CodeCompletionString::Chunk
CodeCompletionString::Chunk::CreateCurrentParameter(
const char *CurrentParameter) {
return Chunk(CK_CurrentParameter, CurrentParameter);
}
void
CodeCompletionString::Chunk::Destroy() {
switch (Kind) {
@ -67,10 +123,23 @@ CodeCompletionString::Chunk::Destroy() {
delete Optional;
break;
case CK_TypedText:
case CK_Text:
case CK_Placeholder:
case CK_Informative:
delete [] Text;
case CK_CurrentParameter:
delete [] Text;
break;
case CK_LeftParen:
case CK_RightParen:
case CK_LeftBracket:
case CK_RightBracket:
case CK_LeftBrace:
case CK_RightBrace:
case CK_LeftAngle:
case CK_RightAngle:
case CK_Comma:
break;
}
}
@ -86,16 +155,322 @@ std::string CodeCompletionString::getAsString() const {
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->Text << "#>"; break;
case CK_Informative: OS << "[#" << C->Text << "#]"; break;
case CK_CurrentParameter: OS << "<#" << C->Text << "#>"; break;
default: OS << C->Text; break;
}
}
OS.flush();
return Result;
}
namespace {
// Escape a string for XML-like formatting.
struct EscapedString {
EscapedString(llvm::StringRef Str) : Str(Str) { }
llvm::StringRef Str;
};
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, EscapedString EStr) {
llvm::StringRef Str = EStr.Str;
while (!Str.empty()) {
// Find the next escaped character.
llvm::StringRef::size_type Pos = Str.find_first_of("<>&\"'");
// Print everything before that escaped character.
OS << Str.substr(0, Pos);
// If we didn't find any escaped characters, we're done.
if (Pos == llvm::StringRef::npos)
break;
// Print the appropriate escape sequence.
switch (Str[Pos]) {
case '<': OS << "&lt;"; break;
case '>': OS << "&gt;"; break;
case '&': OS << "&amp;"; break;
case '"': OS << "&quot;"; break;
case '\'': OS << "&apos;"; break;
}
// Remove everything up to and including that escaped character.
Str = Str.substr(Pos + 1);
}
return OS;
}
/// \brief Remove XML-like escaping from a string.
std::string UnescapeString(llvm::StringRef Str) {
using llvm::StringRef;
std::string Result;
llvm::raw_string_ostream OS(Result);
while (!Str.empty()) {
StringRef::size_type Amp = Str.find('&');
OS << Str.substr(0, Amp);
if (Amp == StringRef::npos)
break;
StringRef::size_type Semi = Str.substr(Amp).find(';');
if (Semi == StringRef::npos) {
// Malformed input; do the best we can.
OS << '&';
Str = Str.substr(Amp + 1);
continue;
}
char Unescaped = llvm::StringSwitch<char>(Str.substr(Amp + 1, Semi - 1))
.Case("lt", '<')
.Case("gt", '>')
.Case("amp", '&')
.Case("quot", '"')
.Case("apos", '\'')
.Default('\0');
if (Unescaped)
OS << Unescaped;
else
OS << Str.substr(Amp, Semi + 1);
Str = Str.substr(Amp + Semi + 1);
}
return OS.str();
}
}
void CodeCompletionString::Serialize(llvm::raw_ostream &OS) const {
for (iterator C = begin(), CEnd = end(); C != CEnd; ++C) {
switch (C->Kind) {
case CK_TypedText:
OS << "<typed-text>" << EscapedString(C->Text) << "</>";
break;
case CK_Text:
OS << "<text>" << EscapedString(C->Text) << "</>";
break;
case CK_Optional:
OS << "<optional>";
C->Optional->Serialize(OS);
OS << "</>";
break;
case CK_Placeholder:
OS << "<placeholder>" << EscapedString(C->Text) << "</>";
break;
case CK_Informative:
OS << "<informative>" << EscapedString(C->Text) << "</>";
break;
case CK_CurrentParameter:
OS << "<current-parameter>" << EscapedString(C->Text) << "</>";
break;
case CK_LeftParen:
OS << "<lparen/>";
break;
case CK_RightParen:
OS << "<rparen/>";
break;
case CK_LeftBracket:
OS << "<lbracket/>";
break;
case CK_RightBracket:
OS << "<rbracket/>";
break;
case CK_LeftBrace:
OS << "<lbrace/>";
break;
case CK_RightBrace:
OS << "<rbrace/>";
break;
case CK_LeftAngle:
OS << "<langle/>";
break;
case CK_RightAngle:
OS << "<rangle/>";
break;
case CK_Comma:
OS << "<comma/>";
break;
}
}
}
/// \brief Parse the next XML-ish tag of the form <blah>.
///
/// \param Str the string in which we're looking for the next tag.
///
/// \param TagPos if successful, will be set to the start of the tag we found.
///
/// \param Standalone will indicate whether this is a "standalone" tag that
/// has no associated data, e.g., <comma/>.
///
/// \param Terminator will indicate whether this is a terminating tag (that is
/// or starts with '/').
///
/// \returns the tag itself, without the angle brackets.
static llvm::StringRef ParseNextTag(llvm::StringRef Str,
llvm::StringRef::size_type &StartTag,
llvm::StringRef::size_type &AfterTag,
bool &Standalone, bool &Terminator) {
using llvm::StringRef;
Standalone = false;
Terminator = false;
AfterTag = StringRef::npos;
// Find the starting '<'.
StartTag = Str.find('<');
if (StartTag == StringRef::npos)
return llvm::StringRef();
// Find the corresponding '>'.
llvm::StringRef::size_type EndTag = Str.substr(StartTag).find('>');
if (EndTag == StringRef::npos)
return llvm::StringRef();
AfterTag = StartTag + EndTag + 1;
// Determine whether this is a terminating tag.
if (Str[StartTag + 1] == '/') {
Terminator = true;
Str = Str.substr(1);
--EndTag;
}
// Determine whether this is a standalone tag.
if (!Terminator && Str[StartTag + EndTag - 1] == '/') {
Standalone = true;
if (EndTag > 1)
--EndTag;
}
return Str.substr(StartTag + 1, EndTag - 1);
}
CodeCompletionString *CodeCompletionString::Deserialize(llvm::StringRef &Str) {
using llvm::StringRef;
CodeCompletionString *Result = new CodeCompletionString;
do {
// Parse the next tag.
StringRef::size_type StartTag, AfterTag;
bool Standalone, Terminator;
StringRef Tag = ParseNextTag(Str, StartTag, AfterTag, Standalone,
Terminator);
if (StartTag == StringRef::npos)
break;
// Figure out what kind of chunk we have.
const unsigned UnknownKind = 10000;
unsigned Kind = llvm::StringSwitch<unsigned>(Tag)
.Case("typed-text", CK_TypedText)
.Case("text", CK_Text)
.Case("optional", CK_Optional)
.Case("placeholder", CK_Placeholder)
.Case("informative", CK_Informative)
.Case("current-parameter", CK_CurrentParameter)
.Case("lparen", CK_LeftParen)
.Case("rparen", CK_RightParen)
.Case("lbracket", CK_LeftBracket)
.Case("rbracket", CK_RightBracket)
.Case("lbrace", CK_LeftBrace)
.Case("rbrace", CK_RightBrace)
.Case("langle", CK_LeftAngle)
.Case("rangle", CK_RightAngle)
.Case("comma", CK_Comma)
.Default(UnknownKind);
// If we've hit a terminator tag, we're done.
if (Terminator)
break;
// Consume the tag.
Str = Str.substr(AfterTag);
// Handle standalone tags now, since they don't need to be matched to
// anything.
if (Standalone) {
// Ignore anything we don't know about.
if (Kind == UnknownKind)
continue;
switch ((ChunkKind)Kind) {
case CK_TypedText:
case CK_Text:
case CK_Optional:
case CK_Placeholder:
case CK_Informative:
case CK_CurrentParameter:
// There is no point in creating empty chunks of these kinds.
break;
case CK_LeftParen:
case CK_RightParen:
case CK_LeftBracket:
case CK_RightBracket:
case CK_LeftBrace:
case CK_RightBrace:
case CK_LeftAngle:
case CK_RightAngle:
case CK_Comma:
Result->AddChunk(Chunk((ChunkKind)Kind));
break;
}
continue;
}
if (Kind == CK_Optional) {
// Deserialize the optional code-completion string.
std::auto_ptr<CodeCompletionString> Optional(Deserialize(Str));
Result->AddOptionalChunk(Optional);
}
StringRef EndTag = ParseNextTag(Str, StartTag, AfterTag, Standalone,
Terminator);
if (StartTag == StringRef::npos || !Terminator || Standalone)
break; // Parsing failed; just give up.
if (EndTag.empty() || Tag == EndTag) {
// Found the matching end tag. Add this chunk based on the text
// between the tags, then consume that input.
StringRef Text = Str.substr(0, StartTag);
switch ((ChunkKind)Kind) {
case CK_TypedText:
case CK_Text:
case CK_Placeholder:
case CK_Informative:
case CK_CurrentParameter:
case CK_LeftParen:
case CK_RightParen:
case CK_LeftBracket:
case CK_RightBracket:
case CK_LeftBrace:
case CK_RightBrace:
case CK_LeftAngle:
case CK_RightAngle:
case CK_Comma:
Result->AddChunk(Chunk((ChunkKind)Kind, UnescapeString(Text)));
break;
case CK_Optional:
// We've already added the optional chunk.
break;
}
}
// Remove this tag.
Str = Str.substr(AfterTag);
} while (!Str.empty());
return Result;
}
//===----------------------------------------------------------------------===//
// Code completion overload candidate implementation
//===----------------------------------------------------------------------===//
@ -193,3 +568,85 @@ PrintingCodeCompleteConsumer::ProcessOverloadCandidates(unsigned CurrentArg,
// FIXME: Move this somewhere else!
SemaRef.PP.getDiagnostics().setSuppressAllDiagnostics();
}
void
CIndexCodeCompleteConsumer::ProcessCodeCompleteResults(Result *Results,
unsigned NumResults) {
// Print the results.
for (unsigned I = 0; I != NumResults; ++I) {
OS << "COMPLETION:" << Results[I].Rank << ":";
switch (Results[I].Kind) {
case Result::RK_Declaration:
if (RecordDecl *Record = dyn_cast<RecordDecl>(Results[I].Declaration)) {
if (Record->isStruct())
OS << "Struct:";
else if (Record->isUnion())
OS << "Union:";
else
OS << "Class:";
} else if (ObjCMethodDecl *Method
= dyn_cast<ObjCMethodDecl>(Results[I].Declaration)) {
if (Method->isInstanceMethod())
OS << "ObjCInstanceMethod:";
else
OS << "ObjCClassMethod:";
} else {
OS << Results[I].Declaration->getDeclKindName() << ":";
}
if (CodeCompletionString *CCS
= Results[I].CreateCodeCompletionString(SemaRef)) {
CCS->Serialize(OS);
delete CCS;
} else {
OS << "<typed-text>"
<< Results[I].Declaration->getNameAsString()
<< "</>";
}
OS << '\n';
break;
case Result::RK_Keyword:
OS << "Keyword:<typed-text>" << Results[I].Keyword << "</>\n";
break;
case Result::RK_Macro: {
OS << "Macro:";
if (CodeCompletionString *CCS
= Results[I].CreateCodeCompletionString(SemaRef)) {
CCS->Serialize(OS);
delete CCS;
} else {
OS << "<typed-text>" << Results[I].Macro->getName() << "</>";
}
OS << '\n';
break;
}
}
}
// Once we've printed the code-completion results, suppress remaining
// diagnostics.
// FIXME: Move this somewhere else!
SemaRef.PP.getDiagnostics().setSuppressAllDiagnostics();
}
void
CIndexCodeCompleteConsumer::ProcessOverloadCandidates(unsigned CurrentArg,
OverloadCandidate *Candidates,
unsigned NumCandidates) {
for (unsigned I = 0; I != NumCandidates; ++I) {
if (CodeCompletionString *CCS
= Candidates[I].CreateSignatureString(CurrentArg, SemaRef)) {
OS << "OVERLOAD:";
CCS->Serialize(OS);
OS << '\n';
delete CCS;
}
}
// Once we've printed the code-completion results, suppress remaining
// diagnostics.
// FIXME: Move this somewhere else!
SemaRef.PP.getDiagnostics().setSuppressAllDiagnostics();
}

View File

@ -187,8 +187,7 @@ getRequiredQualification(ASTContext &Context,
Context.getTypeDeclType(TD).getTypePtr());
else
assert(Parent->isTranslationUnit());
}
}
return Result;
}
@ -674,6 +673,8 @@ static void AddTypeSpecifierResults(const LangOptions &LangOpts, unsigned Rank,
static void AddFunctionParameterChunks(ASTContext &Context,
FunctionDecl *Function,
CodeCompletionString *Result) {
typedef CodeCompletionString::Chunk Chunk;
CodeCompletionString *CCStr = Result;
for (unsigned P = 0, N = Function->getNumParams(); P != N; ++P) {
@ -688,7 +689,7 @@ static void AddFunctionParameterChunks(ASTContext &Context,
}
if (P != 0)
CCStr->AddTextChunk(", ");
CCStr->AddChunk(Chunk(CodeCompletionString::CK_Comma));
// Format the placeholder string.
std::string PlaceholderStr;
@ -713,6 +714,8 @@ static void AddTemplateParameterChunks(ASTContext &Context,
TemplateDecl *Template,
CodeCompletionString *Result,
unsigned MaxParameters = 0) {
typedef CodeCompletionString::Chunk Chunk;
CodeCompletionString *CCStr = Result;
bool FirstParameter = true;
@ -768,7 +771,7 @@ static void AddTemplateParameterChunks(ASTContext &Context,
if (FirstParameter)
FirstParameter = false;
else
CCStr->AddTextChunk(", ");
CCStr->AddChunk(Chunk(CodeCompletionString::CK_Comma));
// Add the placeholder string.
CCStr->AddPlaceholderChunk(PlaceholderStr.c_str());
@ -803,6 +806,8 @@ void AddQualifierToCompletionString(CodeCompletionString *Result,
/// result is all that is needed.
CodeCompletionString *
CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) {
typedef CodeCompletionString::Chunk Chunk;
if (Kind == RK_Keyword)
return 0;
@ -813,12 +818,12 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) {
// Format a function-like macro with placeholders for the arguments.
CodeCompletionString *Result = new CodeCompletionString;
Result->AddTextChunk(Macro->getName().str().c_str());
Result->AddTextChunk("(");
Result->AddTypedTextChunk(Macro->getName().str().c_str());
Result->AddChunk(Chunk(CodeCompletionString::CK_LeftParen));
for (MacroInfo::arg_iterator A = MI->arg_begin(), AEnd = MI->arg_end();
A != AEnd; ++A) {
if (A != MI->arg_begin())
Result->AddTextChunk(", ");
Result->AddChunk(Chunk(CodeCompletionString::CK_Comma));
if (!MI->isVariadic() || A != AEnd - 1) {
// Non-variadic argument.
@ -837,21 +842,28 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) {
Result->AddPlaceholderChunk(Arg.c_str());
}
}
Result->AddTextChunk(")");
Result->AddChunk(Chunk(CodeCompletionString::CK_RightParen));
return Result;
}
assert(Kind == RK_Declaration && "Missed a macro kind?");
NamedDecl *ND = Declaration;
if (StartsNestedNameSpecifier) {
CodeCompletionString *Result = new CodeCompletionString;
Result->AddTypedTextChunk(ND->getNameAsString().c_str());
Result->AddTextChunk("::");
return Result;
}
if (FunctionDecl *Function = dyn_cast<FunctionDecl>(ND)) {
CodeCompletionString *Result = new CodeCompletionString;
AddQualifierToCompletionString(Result, Qualifier, QualifierIsInformative,
S.Context);
Result->AddTextChunk(Function->getNameAsString().c_str());
Result->AddTextChunk("(");
Result->AddTypedTextChunk(Function->getNameAsString().c_str());
Result->AddChunk(Chunk(CodeCompletionString::CK_LeftParen));
AddFunctionParameterChunks(S.Context, Function, Result);
Result->AddTextChunk(")");
Result->AddChunk(Chunk(CodeCompletionString::CK_RightParen));
return Result;
}
@ -860,7 +872,7 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) {
AddQualifierToCompletionString(Result, Qualifier, QualifierIsInformative,
S.Context);
FunctionDecl *Function = FunTmpl->getTemplatedDecl();
Result->AddTextChunk(Function->getNameAsString().c_str());
Result->AddTypedTextChunk(Function->getNameAsString().c_str());
// Figure out which template parameters are deduced (or have default
// arguments).
@ -884,7 +896,7 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) {
else {
assert(isa<TemplateTemplateParmDecl>(Param));
HasDefaultArg
= cast<TemplateTemplateParmDecl>(Param)->hasDefaultArgument();
= cast<TemplateTemplateParmDecl>(Param)->hasDefaultArgument();
}
if (!HasDefaultArg)
@ -896,16 +908,16 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) {
// Some of the function template arguments cannot be deduced from a
// function call, so we introduce an explicit template argument list
// containing all of the arguments up to the first deducible argument.
Result->AddTextChunk("<");
Result->AddChunk(Chunk(CodeCompletionString::CK_LeftAngle));
AddTemplateParameterChunks(S.Context, FunTmpl, Result,
LastDeducibleArgument);
Result->AddTextChunk(">");
Result->AddChunk(Chunk(CodeCompletionString::CK_RightAngle));
}
// Add the function parameters
Result->AddTextChunk("(");
Result->AddChunk(Chunk(CodeCompletionString::CK_LeftParen));
AddFunctionParameterChunks(S.Context, Function, Result);
Result->AddTextChunk(")");
Result->AddChunk(Chunk(CodeCompletionString::CK_RightParen));
return Result;
}
@ -913,20 +925,18 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) {
CodeCompletionString *Result = new CodeCompletionString;
AddQualifierToCompletionString(Result, Qualifier, QualifierIsInformative,
S.Context);
Result->AddTextChunk(Template->getNameAsString().c_str());
Result->AddTextChunk("<");
Result->AddTypedTextChunk(Template->getNameAsString().c_str());
Result->AddChunk(Chunk(CodeCompletionString::CK_LeftAngle));
AddTemplateParameterChunks(S.Context, Template, Result);
Result->AddTextChunk(">");
Result->AddChunk(Chunk(CodeCompletionString::CK_RightAngle));
return Result;
}
if (Qualifier || StartsNestedNameSpecifier) {
if (Qualifier) {
CodeCompletionString *Result = new CodeCompletionString;
AddQualifierToCompletionString(Result, Qualifier, QualifierIsInformative,
S.Context);
Result->AddTextChunk(ND->getNameAsString().c_str());
if (StartsNestedNameSpecifier)
Result->AddTextChunk("::");
Result->AddTypedTextChunk(ND->getNameAsString().c_str());
return Result;
}
@ -937,6 +947,8 @@ CodeCompletionString *
CodeCompleteConsumer::OverloadCandidate::CreateSignatureString(
unsigned CurrentArg,
Sema &S) const {
typedef CodeCompletionString::Chunk Chunk;
CodeCompletionString *Result = new CodeCompletionString;
FunctionDecl *FDecl = getFunction();
const FunctionProtoType *Proto
@ -947,9 +959,9 @@ CodeCompleteConsumer::OverloadCandidate::CreateSignatureString(
const FunctionType *FT = getFunctionType();
Result->AddTextChunk(
FT->getResultType().getAsString(S.Context.PrintingPolicy).c_str());
Result->AddTextChunk("(");
Result->AddPlaceholderChunk("...");
Result->AddTextChunk("(");
Result->AddChunk(Chunk(CodeCompletionString::CK_LeftParen));
Result->AddChunk(Chunk(CodeCompletionString::CK_CurrentParameter, "..."));
Result->AddChunk(Chunk(CodeCompletionString::CK_RightParen));
return Result;
}
@ -959,11 +971,11 @@ CodeCompleteConsumer::OverloadCandidate::CreateSignatureString(
Result->AddTextChunk(
Proto->getResultType().getAsString(S.Context.PrintingPolicy).c_str());
Result->AddTextChunk("(");
Result->AddChunk(Chunk(CodeCompletionString::CK_LeftParen));
unsigned NumParams = FDecl? FDecl->getNumParams() : Proto->getNumArgs();
for (unsigned I = 0; I != NumParams; ++I) {
if (I)
Result->AddTextChunk(", ");
Result->AddChunk(Chunk(CodeCompletionString::CK_Comma));
std::string ArgString;
QualType ArgType;
@ -978,19 +990,20 @@ CodeCompleteConsumer::OverloadCandidate::CreateSignatureString(
ArgType.getAsStringInternal(ArgString, S.Context.PrintingPolicy);
if (I == CurrentArg)
Result->AddPlaceholderChunk(ArgString.c_str());
Result->AddChunk(Chunk(CodeCompletionString::CK_CurrentParameter,
ArgString.c_str()));
else
Result->AddTextChunk(ArgString.c_str());
}
if (Proto && Proto->isVariadic()) {
Result->AddTextChunk(", ");
Result->AddChunk(Chunk(CodeCompletionString::CK_Comma));
if (CurrentArg < NumParams)
Result->AddTextChunk("...");
else
Result->AddPlaceholderChunk("...");
Result->AddChunk(Chunk(CodeCompletionString::CK_CurrentParameter, "..."));
}
Result->AddTextChunk(")");
Result->AddChunk(Chunk(CodeCompletionString::CK_RightParen));
return Result;
}
@ -1049,11 +1062,11 @@ namespace {
};
}
// Add all of the known macros as code-completion results.
static void AddMacroResults(Preprocessor &PP, unsigned Rank,
ResultBuilder &Results) {
Results.EnterNewScope();
for (Preprocessor::macro_iterator M = PP.macro_begin(), MEnd = PP.macro_end();
for (Preprocessor::macro_iterator M = PP.macro_begin(),
MEnd = PP.macro_end();
M != MEnd; ++M)
Results.MaybeAddResult(CodeCompleteConsumer::Result(M->first, Rank));
Results.ExitScope();
@ -1073,7 +1086,8 @@ void Sema::CodeCompleteOrdinaryName(Scope *S) {
ResultBuilder Results(*this, &ResultBuilder::IsOrdinaryName);
unsigned NextRank = CollectLookupResults(S, Context.getTranslationUnitDecl(),
0, CurContext, Results);
AddMacroResults(PP, NextRank, Results);
if (CodeCompleter->includeMacros())
AddMacroResults(PP, NextRank, Results);
HandleCodeCompleteResults(CodeCompleter, Results.data(), Results.size());
}
@ -1130,7 +1144,8 @@ void Sema::CodeCompleteMemberReferenceExpr(Scope *S, ExprTy *BaseE,
}
// Add macros
AddMacroResults(PP, NextRank, Results);
if (CodeCompleter->includeMacros())
AddMacroResults(PP, NextRank, Results);
// Hand off the results found for code completion.
HandleCodeCompleteResults(CodeCompleter, Results.data(), Results.size());
@ -1177,7 +1192,8 @@ void Sema::CodeCompleteTag(Scope *S, unsigned TagSpec) {
NextRank, CurContext, Results);
}
AddMacroResults(PP, NextRank, Results);
if (CodeCompleter->includeMacros())
AddMacroResults(PP, NextRank, Results);
HandleCodeCompleteResults(CodeCompleter, Results.data(), Results.size());
}
@ -1255,7 +1271,8 @@ void Sema::CodeCompleteCase(Scope *S) {
}
Results.ExitScope();
AddMacroResults(PP, 1, Results);
if (CodeCompleter->includeMacros())
AddMacroResults(PP, 1, Results);
HandleCodeCompleteResults(CodeCompleter, Results.data(), Results.size());
}
@ -1350,7 +1367,8 @@ void Sema::CodeCompleteQualifiedId(Scope *S, const CXXScopeSpec &SS,
if (!Results.empty() && NNS->isDependent())
Results.MaybeAddResult(CodeCompleteConsumer::Result("template", NextRank));
AddMacroResults(PP, NextRank + 1, Results);
if (CodeCompleter->includeMacros())
AddMacroResults(PP, NextRank + 1, Results);
HandleCodeCompleteResults(CodeCompleter, Results.data(), Results.size());
}
@ -1371,7 +1389,8 @@ void Sema::CodeCompleteUsing(Scope *S) {
0, CurContext, Results);
Results.ExitScope();
AddMacroResults(PP, NextRank, Results);
if (CodeCompleter->includeMacros())
AddMacroResults(PP, NextRank, Results);
HandleCodeCompleteResults(CodeCompleter, Results.data(), Results.size());
}
@ -1386,7 +1405,8 @@ void Sema::CodeCompleteUsingDirective(Scope *S) {
unsigned NextRank = CollectLookupResults(S, Context.getTranslationUnitDecl(),
0, CurContext, Results);
Results.ExitScope();
AddMacroResults(PP, NextRank, Results);
if (CodeCompleter->includeMacros())
AddMacroResults(PP, NextRank, Results);
HandleCodeCompleteResults(CodeCompleter, Results.data(), Results.size());
}
@ -1421,7 +1441,8 @@ void Sema::CodeCompleteNamespaceDecl(Scope *S) {
Results.ExitScope();
}
AddMacroResults(PP, 1, Results);
if (CodeCompleter->includeMacros())
AddMacroResults(PP, 1, Results);
HandleCodeCompleteResults(CodeCompleter, Results.data(), Results.size());
}
@ -1433,7 +1454,8 @@ void Sema::CodeCompleteNamespaceAliasDecl(Scope *S) {
ResultBuilder Results(*this, &ResultBuilder::IsNamespaceOrAlias);
unsigned NextRank = CollectLookupResults(S, Context.getTranslationUnitDecl(),
0, CurContext, Results);
AddMacroResults(PP, NextRank, Results);
if (CodeCompleter->includeMacros())
AddMacroResults(PP, NextRank, Results);
HandleCodeCompleteResults(CodeCompleter, Results.data(), Results.size());
}
@ -1464,7 +1486,8 @@ void Sema::CodeCompleteOperatorName(Scope *S) {
NextRank + 1, CurContext, Results);
Results.ExitScope();
AddMacroResults(PP, NextRank, Results);
if (CodeCompleter->includeMacros())
AddMacroResults(PP, NextRank, Results);
HandleCodeCompleteResults(CodeCompleter, Results.data(), Results.size());
}

View File

@ -13,9 +13,9 @@ struct Point {
};
void test(struct Point *p) {
// RUN: clang-cc -fsyntax-only -code-completion-at=%s:17:14 %s -o - | FileCheck -check-prefix=CC1 %s &&
// RUN: clang-cc -fsyntax-only -code-completion-macros -code-completion-at=%s:17:14 %s -o - | FileCheck -check-prefix=CC1 %s &&
switch (p->IDENTITY(color)) {
// RUN: clang-cc -fsyntax-only -code-completion-at=%s:19:9 %s -o - | FileCheck -check-prefix=CC2 %s &&
// RUN: clang-cc -fsyntax-only -code-completion-macros -code-completion-at=%s:19:9 %s -o - | FileCheck -check-prefix=CC2 %s &&
case
}
// CC1: color

View File

@ -16,12 +16,15 @@
#include "clang/Index/Indexer.h"
#include "clang/Index/ASTLocation.h"
#include "clang/Index/Utils.h"
#include "clang/Sema/CodeCompleteConsumer.h"
#include "clang/AST/DeclVisitor.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/Decl.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Frontend/ASTUnit.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Config/config.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/MemoryBuffer.h"
@ -31,6 +34,7 @@
#include <cstdio>
#include <vector>
#include <sstream>
#ifdef LLVM_ON_WIN32
#define WIN32_LEAN_AND_MEAN
@ -993,5 +997,316 @@ void clang_getDefinitionSpellingAndExtent(CXCursor C,
*endColumn = SM.getSpellingColumnNumber(Body->getRBracLoc());
}
enum CXCompletionChunkKind
clang_getCompletionChunkKind(CXCompletionString completion_string,
unsigned chunk_number) {
CodeCompletionString *CCStr = (CodeCompletionString *)completion_string;
if (!CCStr || chunk_number >= CCStr->size())
return CXCompletionChunk_Text;
switch ((*CCStr)[chunk_number].Kind) {
case CodeCompletionString::CK_TypedText:
return CXCompletionChunk_TypedText;
case CodeCompletionString::CK_Text:
return CXCompletionChunk_Text;
case CodeCompletionString::CK_Optional:
return CXCompletionChunk_Optional;
case CodeCompletionString::CK_Placeholder:
return CXCompletionChunk_Placeholder;
case CodeCompletionString::CK_Informative:
return CXCompletionChunk_Informative;
case CodeCompletionString::CK_CurrentParameter:
return CXCompletionChunk_CurrentParameter;
case CodeCompletionString::CK_LeftParen:
return CXCompletionChunk_LeftParen;
case CodeCompletionString::CK_RightParen:
return CXCompletionChunk_RightParen;
case CodeCompletionString::CK_LeftBracket:
return CXCompletionChunk_LeftBracket;
case CodeCompletionString::CK_RightBracket:
return CXCompletionChunk_RightBracket;
case CodeCompletionString::CK_LeftBrace:
return CXCompletionChunk_LeftBrace;
case CodeCompletionString::CK_RightBrace:
return CXCompletionChunk_RightBrace;
case CodeCompletionString::CK_LeftAngle:
return CXCompletionChunk_LeftAngle;
case CodeCompletionString::CK_RightAngle:
return CXCompletionChunk_RightAngle;
case CodeCompletionString::CK_Comma:
return CXCompletionChunk_Comma;
}
// Should be unreachable, but let's be careful.
return CXCompletionChunk_Text;
}
const char *clang_getCompletionChunkText(CXCompletionString completion_string,
unsigned chunk_number) {
CodeCompletionString *CCStr = (CodeCompletionString *)completion_string;
if (!CCStr || chunk_number >= CCStr->size())
return 0;
switch ((*CCStr)[chunk_number].Kind) {
case CodeCompletionString::CK_TypedText:
case CodeCompletionString::CK_Text:
case CodeCompletionString::CK_Placeholder:
case CodeCompletionString::CK_CurrentParameter:
case CodeCompletionString::CK_Informative:
case CodeCompletionString::CK_LeftParen:
case CodeCompletionString::CK_RightParen:
case CodeCompletionString::CK_LeftBracket:
case CodeCompletionString::CK_RightBracket:
case CodeCompletionString::CK_LeftBrace:
case CodeCompletionString::CK_RightBrace:
case CodeCompletionString::CK_LeftAngle:
case CodeCompletionString::CK_RightAngle:
case CodeCompletionString::CK_Comma:
return (*CCStr)[chunk_number].Text;
case CodeCompletionString::CK_Optional:
// Note: treated as an empty text block.
return 0;
}
// Should be unreachable, but let's be careful.
return 0;
}
CXCompletionString
clang_getCompletionChunkCompletionString(CXCompletionString completion_string,
unsigned chunk_number) {
CodeCompletionString *CCStr = (CodeCompletionString *)completion_string;
if (!CCStr || chunk_number >= CCStr->size())
return 0;
switch ((*CCStr)[chunk_number].Kind) {
case CodeCompletionString::CK_TypedText:
case CodeCompletionString::CK_Text:
case CodeCompletionString::CK_Placeholder:
case CodeCompletionString::CK_CurrentParameter:
case CodeCompletionString::CK_Informative:
case CodeCompletionString::CK_LeftParen:
case CodeCompletionString::CK_RightParen:
case CodeCompletionString::CK_LeftBracket:
case CodeCompletionString::CK_RightBracket:
case CodeCompletionString::CK_LeftBrace:
case CodeCompletionString::CK_RightBrace:
case CodeCompletionString::CK_LeftAngle:
case CodeCompletionString::CK_RightAngle:
case CodeCompletionString::CK_Comma:
return 0;
case CodeCompletionString::CK_Optional:
// Note: treated as an empty text block.
return (*CCStr)[chunk_number].Optional;
}
// Should be unreachable, but let's be careful.
return 0;
}
unsigned clang_getNumCompletionChunks(CXCompletionString completion_string) {
CodeCompletionString *CCStr = (CodeCompletionString *)completion_string;
return CCStr? CCStr->size() : 0;
}
static CXCursorKind parseResultKind(llvm::StringRef Str) {
return llvm::StringSwitch<CXCursorKind>(Str)
.Case("Typedef", CXCursor_TypedefDecl)
.Case("Struct", CXCursor_StructDecl)
.Case("Union", CXCursor_UnionDecl)
.Case("Class", CXCursor_ClassDecl)
.Case("Field", CXCursor_FieldDecl)
.Case("EnumConstant", CXCursor_EnumConstantDecl)
.Case("Function", CXCursor_FunctionDecl)
// FIXME: Hacks here to make C++ member functions look like C functions
.Case("CXXMethod", CXCursor_FunctionDecl)
.Case("CXXConstructor", CXCursor_FunctionDecl)
.Case("CXXDestructor", CXCursor_FunctionDecl)
.Case("CXXConversion", CXCursor_FunctionDecl)
.Case("Var", CXCursor_VarDecl)
.Case("ParmVar", CXCursor_ParmDecl)
.Case("ObjCInterface", CXCursor_ObjCInterfaceDecl)
.Case("ObjCCategory", CXCursor_ObjCCategoryDecl)
.Case("ObjCProtocol", CXCursor_ObjCProtocolDecl)
.Case("ObjCProperty", CXCursor_ObjCPropertyDecl)
.Case("ObjCIvar", CXCursor_ObjCIvarDecl)
.Case("ObjCInstanceMethod", CXCursor_ObjCInstanceMethodDecl)
.Case("ObjCClassMethod", CXCursor_ObjCClassMethodDecl)
.Default(CXCursor_NotImplemented);
}
void clang_codeComplete(CXIndex CIdx,
const char *source_filename,
int num_command_line_args,
const char **command_line_args,
const char *complete_filename,
unsigned complete_line,
unsigned complete_column,
CXCompletionIterator completion_iterator,
CXClientData client_data) {
// The indexer, which is mainly used to determine where diagnostics go.
CIndexer *CXXIdx = static_cast<CIndexer *>(CIdx);
// Build up the arguments for invoking 'clang'.
std::vector<const char *> argv;
// First add the complete path to the 'clang' executable.
llvm::sys::Path ClangPath = CXXIdx->getClangPath();
argv.push_back(ClangPath.c_str());
// Add the '-fsyntax-only' argument so that we only perform a basic
// syntax check of the code.
argv.push_back("-fsyntax-only");
// Add the appropriate '-code-completion-at=file:line:column' argument
// to perform code completion, with an "-Xclang" preceding it.
std::string code_complete_at;
code_complete_at += "-code-completion-at=";
code_complete_at += complete_filename;
code_complete_at += ":";
code_complete_at += llvm::utostr(complete_line);
code_complete_at += ":";
code_complete_at += llvm::utostr(complete_column);
argv.push_back("-Xclang");
argv.push_back(code_complete_at.c_str());
argv.push_back("-Xclang");
argv.push_back("-code-completion-printer=cindex");
// Add the source file name (FIXME: later, we'll want to build temporary
// file from the buffer, or just feed the source text via standard input).
argv.push_back(source_filename);
// Process the compiler options, stripping off '-o', '-c', '-fsyntax-only'.
for (int i = 0; i < num_command_line_args; ++i)
if (const char *arg = command_line_args[i]) {
if (strcmp(arg, "-o") == 0) {
++i; // Also skip the matching argument.
continue;
}
if (strcmp(arg, "-emit-ast") == 0 ||
strcmp(arg, "-c") == 0 ||
strcmp(arg, "-fsyntax-only") == 0) {
continue;
}
// Keep the argument.
argv.push_back(arg);
}
// Add the null terminator.
argv.push_back(NULL);
// Generate a temporary name for the AST file.
char tmpFile[L_tmpnam];
char *tmpFileName = tmpnam(tmpFile);
llvm::sys::Path ResultsFile(tmpFileName);
// Invoke 'clang'.
llvm::sys::Path DevNull; // leave empty, causes redirection to /dev/null
// on Unix or NUL (Windows).
std::string ErrMsg;
const llvm::sys::Path *Redirects[] = { &DevNull, &ResultsFile, &DevNull, 0 };
llvm::sys::Program::ExecuteAndWait(ClangPath, &argv[0], /* env */ NULL,
/* redirects */ &Redirects[0],
/* secondsToWait */ 0,
/* memoryLimits */ 0, &ErrMsg);
if (!ErrMsg.empty()) {
llvm::errs() << "clang_codeComplete: " << ErrMsg
<< '\n' << "Arguments: \n";
for (std::vector<const char*>::iterator I = argv.begin(), E = argv.end();
I!=E; ++I) {
if (*I)
llvm::errs() << ' ' << *I << '\n';
}
llvm::errs() << '\n';
}
// Parse the resulting source file to find code-completion results.
using llvm::MemoryBuffer;
using llvm::StringRef;
if (MemoryBuffer *F = MemoryBuffer::getFile(ResultsFile.c_str())) {
StringRef Buffer = F->getBuffer();
do {
StringRef::size_type CompletionIdx = Buffer.find("COMPLETION:");
StringRef::size_type OverloadIdx = Buffer.find("OVERLOAD:");
if (CompletionIdx == StringRef::npos && OverloadIdx == StringRef::npos)
break;
if (OverloadIdx < CompletionIdx) {
// Parse an overload result.
Buffer = Buffer.substr(OverloadIdx);
// Skip past the OVERLOAD:
Buffer = Buffer.substr(Buffer.find(':') + 1);
// Find the entire completion string.
StringRef::size_type EOL = Buffer.find_first_of("\n\r");
if (EOL == StringRef::npos)
continue;
StringRef Line = Buffer.substr(0, EOL);
Buffer = Buffer.substr(EOL + 1);
CodeCompletionString *CCStr = CodeCompletionString::Deserialize(Line);
if (!CCStr || CCStr->empty())
continue;
// Vend the code-completion result to the caller.
CXCompletionResult Result;
Result.CursorKind = CXCursor_NotImplemented;
Result.CompletionString = CCStr;
if (completion_iterator)
completion_iterator(&Result, client_data);
delete CCStr;
continue;
}
// Parse a completion result.
Buffer = Buffer.substr(CompletionIdx);
// Skip past the COMPLETION:
Buffer = Buffer.substr(Buffer.find(':') + 1);
// Get the rank
unsigned Rank = 0;
StringRef::size_type AfterRank = Buffer.find(':');
Buffer.substr(0, AfterRank).getAsInteger(10, Rank);
Buffer = Buffer.substr(AfterRank + 1);
// Get the kind of result.
StringRef::size_type AfterKind = Buffer.find(':');
StringRef Kind = Buffer.substr(0, AfterKind);
Buffer = Buffer.substr(AfterKind + 1);
// Skip over any whitespace.
Buffer = Buffer.substr(Buffer.find_first_not_of(" \t"));
// Find the entire completion string.
StringRef::size_type EOL = Buffer.find_first_of("\n\r");
if (EOL == StringRef::npos)
continue;
StringRef Line = Buffer.substr(0, EOL);
Buffer = Buffer.substr(EOL + 1);
CodeCompletionString *CCStr = CodeCompletionString::Deserialize(Line);
if (!CCStr || CCStr->empty())
continue;
// Vend the code-completion result to the caller.
CXCompletionResult Result;
Result.CursorKind = parseResultKind(Kind);
Result.CompletionString = CCStr;
if (completion_iterator)
completion_iterator(&Result, client_data);
delete CCStr;
} while (true);
delete F;
}
ResultsFile.eraseFromDisk();
}
} // end extern "C"

View File

@ -1,8 +1,11 @@
_clang_codeComplete
_clang_createIndex
_clang_createTranslationUnit
_clang_createTranslationUnitFromSourceFile
_clang_disposeIndex
_clang_disposeTranslationUnit
_clang_getCompletionChunkKind
_clang_getCompletionChunkText
_clang_getCursor
_clang_getCursorColumn
_clang_getCursorDecl
@ -24,6 +27,7 @@ _clang_getEntity
_clang_getEntityFromDecl
_clang_getFileName
_clang_getFileTime
_clang_getNumCompletionChunks
_clang_getTranslationUnitSpelling
_clang_getURI
_clang_isDeclaration

View File

@ -1,6 +1,7 @@
/* c-index-test.c */
#include "clang-c/Index.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@ -94,10 +95,115 @@ static void TranslationUnitVisitor(CXTranslationUnit Unit, CXCursor Cursor,
}
}
/* Parse file:line:column from the input string. Returns 0 on success, non-zero
on failure. If successful, the pointer *filename will contain newly-allocated
memory (that will be owned by the caller) to store the file name. */
int parse_file_line_column(const char *input, char **filename, unsigned *line,
unsigned *column) {
const char *colon = strchr(input, ':');
char *endptr = 0;
if (!colon) {
fprintf(stderr, "could not parse filename:line:column in '%s'\n", input);
return 1;
}
/* Copy the file name. */
*filename = (char*)malloc(colon - input);
strncpy(*filename, input, colon - input);
(*filename)[colon - input] = 0;
input = colon + 1;
/* Parse the line number. */
*line = strtol(input, &endptr, 10);
if (*endptr != ':') {
fprintf(stderr, "could not parse line:column in '%s'\n", input);
free(filename);
*filename = 0;
return 1;
}
input = endptr + 1;
/* Parse the column number. */
*column = strtol(input, &endptr, 10);
if (*endptr != 0) {
fprintf(stderr, "could not parse column in '%s'\n", input);
free(filename);
*filename = 0;
return 1;
}
return 0;
}
const char *
clang_getCompletionChunkKindSpelling(enum CXCompletionChunkKind Kind) {
switch (Kind) {
case CXCompletionChunk_Optional: return "Optional";
case CXCompletionChunk_TypedText: return "TypedText";
case CXCompletionChunk_Text: return "Text";
case CXCompletionChunk_Placeholder: return "Placeholder";
case CXCompletionChunk_Informative: return "Informative";
case CXCompletionChunk_CurrentParameter: return "CurrentParameter";
case CXCompletionChunk_LeftParen: return "LeftParen";
case CXCompletionChunk_RightParen: return "RightParen";
case CXCompletionChunk_LeftBracket: return "LeftBracket";
case CXCompletionChunk_RightBracket: return "RightBracket";
case CXCompletionChunk_LeftBrace: return "LeftBrace";
case CXCompletionChunk_RightBrace: return "RightBrace";
case CXCompletionChunk_LeftAngle: return "LeftAngle";
case CXCompletionChunk_RightAngle: return "RightAngle";
case CXCompletionChunk_Comma: return "Comma";
}
return "Unknown";
}
void print_completion_result(CXCompletionResult *completion_result,
CXClientData client_data) {
FILE *file = (FILE *)client_data;
fprintf(file, "%s:",
clang_getCursorKindSpelling(completion_result->CursorKind));
int I, N = clang_getNumCompletionChunks(completion_result->CompletionString);
for (I = 0; I != N; ++I) {
const char *text
= clang_getCompletionChunkText(completion_result->CompletionString, I);
enum CXCompletionChunkKind Kind
= clang_getCompletionChunkKind(completion_result->CompletionString, I);
fprintf(file, "{%s %s}",
clang_getCompletionChunkKindSpelling(Kind),
text? text : "");
}
fprintf(file, "\n");
}
void perform_code_completion(int argc, const char **argv) {
const char *input = argv[1];
char *filename = 0;
unsigned line;
unsigned column;
input += strlen("-code-completion-at=");
if (parse_file_line_column(input, &filename, &line, &column))
return;
CXIndex CIdx = clang_createIndex(0, 0);
clang_codeComplete(CIdx, argv[argc - 1], argc - 3, argv + 2,
filename, line, column, &print_completion_result, stdout);
clang_disposeIndex(CIdx);
free(filename);
}
/*
* First sign of life:-)
*/
int main(int argc, char **argv) {
if (argc > 2 && strstr(argv[1], "-code-completion-at=") == argv[1]) {
perform_code_completion(argc, (const char **)argv);
return 0;
}
if (argc != 3) {
printf("Incorrect usage of c-index-test (requires 3 arguments)\n");
return 0;

View File

@ -215,15 +215,45 @@ OutputFile("o",
llvm::cl::desc("Specify output file"));
enum CodeCompletionPrinter {
CCP_Debug,
CCP_CIndex
};
static llvm::cl::opt<ParsedSourceLocation>
CodeCompletionAt("code-completion-at",
llvm::cl::value_desc("file:line:column"),
llvm::cl::desc("Dump code-completion information at a location"));
static llvm::cl::opt<CodeCompletionPrinter>
CodeCompletionPrinter("code-completion-printer",
llvm::cl::desc("Choose output type:"),
llvm::cl::init(CCP_Debug),
llvm::cl::values(
clEnumValN(CCP_Debug, "debug",
"Debug code-completion results"),
clEnumValN(CCP_CIndex, "cindex",
"Code-completion results for the CIndex library"),
clEnumValEnd));
static llvm::cl::opt<bool>
CodeCompletionWantsMacros("code-completion-macros",
llvm::cl::desc("Include macros in code-completion results"));
/// \brief Buld a new code-completion consumer that prints the results of
/// code completion to standard output.
static CodeCompleteConsumer *BuildPrintingCodeCompleter(Sema &S, void *) {
return new PrintingCodeCompleteConsumer(S, llvm::outs());
switch (CodeCompletionPrinter.getValue()) {
case CCP_Debug:
return new PrintingCodeCompleteConsumer(S, CodeCompletionWantsMacros,
llvm::outs());
case CCP_CIndex:
return new CIndexCodeCompleteConsumer(S, CodeCompletionWantsMacros,
llvm::outs());
};
return 0;
}
//===----------------------------------------------------------------------===//