forked from OSchip/llvm-project
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:
parent
ba78dcd90d
commit
9eb7701dff
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 << "<"; 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
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
Loading…
Reference in New Issue