*/
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
#define LLVM_CLANG_SEMA_CODECOMPLETECONSUMER_H
#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
#include <memory>
#include <string>
/// \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,
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.
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;
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);
/// \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();
};
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.
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 {
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();
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,
#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>
//===----------------------------------------------------------------------===//
// 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
return Chunk(CK_Informative, Informative);
}
+CodeCompletionString::Chunk
+CodeCompletionString::Chunk::CreateCurrentParameter(
+ const char *CurrentParameter) {
+ return Chunk(CK_CurrentParameter, CurrentParameter);
+}
+
+
void
CodeCompletionString::Chunk::Destroy() {
switch (Kind) {
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;
}
}
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 << "<"; break;
+ case '>': OS << ">"; break;
+ case '&': OS << "&"; break;
+ case '"': OS << """; break;
+ case '\'': OS << "'"; 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
//===----------------------------------------------------------------------===//
// 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();
+}
Context.getTypeDeclType(TD).getTypePtr());
else
assert(Parent->isTranslationUnit());
- }
-
+ }
return Result;
}
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) {
}
if (P != 0)
- CCStr->AddTextChunk(", ");
+ CCStr->AddChunk(Chunk(CodeCompletionString::CK_Comma));
// Format the placeholder string.
std::string PlaceholderStr;
TemplateDecl *Template,
CodeCompletionString *Result,
unsigned MaxParameters = 0) {
+ typedef CodeCompletionString::Chunk Chunk;
+
CodeCompletionString *CCStr = Result;
bool FirstParameter = true;
if (FirstParameter)
FirstParameter = false;
else
- CCStr->AddTextChunk(", ");
+ CCStr->AddChunk(Chunk(CodeCompletionString::CK_Comma));
// Add the placeholder string.
CCStr->AddPlaceholderChunk(PlaceholderStr.c_str());
/// result is all that is needed.
CodeCompletionString *
CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) {
+ typedef CodeCompletionString::Chunk Chunk;
+
if (Kind == RK_Keyword)
return 0;
// 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.
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;
}
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).
else {
assert(isa<TemplateTemplateParmDecl>(Param));
HasDefaultArg
- = cast<TemplateTemplateParmDecl>(Param)->hasDefaultArgument();
+ = cast<TemplateTemplateParmDecl>(Param)->hasDefaultArgument();
}
if (!HasDefaultArg)
// 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;
}
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;
}
CodeCompleteConsumer::OverloadCandidate::CreateSignatureString(
unsigned CurrentArg,
Sema &S) const {
+ typedef CodeCompletionString::Chunk Chunk;
+
CodeCompletionString *Result = new CodeCompletionString;
FunctionDecl *FDecl = getFunction();
const FunctionProtoType *Proto
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;
}
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;
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;
}
};
}
-// 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();
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());
}
}
// 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());
NextRank, CurContext, Results);
}
- AddMacroResults(PP, NextRank, Results);
+ if (CodeCompleter->includeMacros())
+ AddMacroResults(PP, NextRank, Results);
HandleCodeCompleteResults(CodeCompleter, Results.data(), Results.size());
}
}
Results.ExitScope();
- AddMacroResults(PP, 1, Results);
+ if (CodeCompleter->includeMacros())
+ AddMacroResults(PP, 1, Results);
HandleCodeCompleteResults(CodeCompleter, Results.data(), Results.size());
}
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());
}
0, CurContext, Results);
Results.ExitScope();
- AddMacroResults(PP, NextRank, Results);
+ if (CodeCompleter->includeMacros())
+ AddMacroResults(PP, NextRank, Results);
HandleCodeCompleteResults(CodeCompleter, Results.data(), Results.size());
}
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());
}
Results.ExitScope();
}
- AddMacroResults(PP, 1, Results);
+ if (CodeCompleter->includeMacros())
+ AddMacroResults(PP, 1, Results);
HandleCodeCompleteResults(CodeCompleter, Results.data(), Results.size());
}
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());
}
NextRank + 1, CurContext, Results);
Results.ExitScope();
- AddMacroResults(PP, NextRank, Results);
+ if (CodeCompleter->includeMacros())
+ AddMacroResults(PP, NextRank, Results);
HandleCodeCompleteResults(CodeCompleter, Results.data(), Results.size());
}
};
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
#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"
#include <cstdio>
#include <vector>
+#include <sstream>
#ifdef LLVM_ON_WIN32
#define WIN32_LEAN_AND_MEAN
*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"
+_clang_codeComplete
_clang_createIndex
_clang_createTranslationUnit
_clang_createTranslationUnitFromSourceFile
_clang_disposeIndex
_clang_disposeTranslationUnit
+_clang_getCompletionChunkKind
+_clang_getCompletionChunkText
_clang_getCursor
_clang_getCursorColumn
_clang_getCursorDecl
_clang_getEntityFromDecl
_clang_getFileName
_clang_getFileTime
+_clang_getNumCompletionChunks
_clang_getTranslationUnitSpelling
_clang_getURI
_clang_isDeclaration
/* c-index-test.c */
#include "clang-c/Index.h"
+#include <stdlib.h>
#include <stdio.h>
#include <string.h>
}
}
+/* 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;
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;
}
//===----------------------------------------------------------------------===//