aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang-c/Index.h321
-rw-r--r--include/clang/Sema/CodeCompleteConsumer.h121
-rw-r--r--lib/Sema/CodeCompleteConsumer.cpp473
-rw-r--r--lib/Sema/SemaCodeComplete.cpp113
-rw-r--r--test/CodeCompletion/macros.c4
-rw-r--r--tools/CIndex/CIndex.cpp315
-rw-r--r--tools/CIndex/CIndex.exports4
-rw-r--r--tools/c-index-test/c-index-test.c106
-rw-r--r--tools/clang-cc/clang-cc.cpp32
9 files changed, 1424 insertions, 65 deletions
diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h
index 1a58f44ff4..7bdcab5292 100644
--- a/include/clang-c/Index.h
+++ b/include/clang-c/Index.h
@@ -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
diff --git a/include/clang/Sema/CodeCompleteConsumer.h b/include/clang/Sema/CodeCompleteConsumer.h
index 5b3522c9eb..9bbf11b162 100644
--- a/include/clang/Sema/CodeCompleteConsumer.h
+++ b/include/clang/Sema/CodeCompleteConsumer.h
@@ -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,
diff --git a/lib/Sema/CodeCompleteConsumer.cpp b/lib/Sema/CodeCompleteConsumer.cpp
index 9b24d55f3e..cb4e8ef826 100644
--- a/lib/Sema/CodeCompleteConsumer.cpp
+++ b/lib/Sema/CodeCompleteConsumer.cpp
@@ -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 completi