[cfe-commits] r86306 - in /cfe/trunk: include/clang-c/Index.h include/clang/Sema/CodeCompleteConsumer.h lib/Sema/CodeCompleteConsumer.cpp lib/Sema/SemaCodeComplete.cpp test/CodeCompletion/macros.c tools/CIndex/CIndex.cpp tools/CIndex/CIndex.exports tools/c-index-test/c-index-test.c tools/clang-cc/clang-cc.cpp

Douglas Gregor dgregor at apple.com
Fri Nov 6 16:00:49 PST 2009


Author: dgregor
Date: Fri Nov  6 18:00:49 2009
New Revision: 86306

URL: http://llvm.org/viewvc/llvm-project?rev=86306&view=rev
Log:
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.

Modified:
    cfe/trunk/include/clang-c/Index.h
    cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h
    cfe/trunk/lib/Sema/CodeCompleteConsumer.cpp
    cfe/trunk/lib/Sema/SemaCodeComplete.cpp
    cfe/trunk/test/CodeCompletion/macros.c
    cfe/trunk/tools/CIndex/CIndex.cpp
    cfe/trunk/tools/CIndex/CIndex.exports
    cfe/trunk/tools/c-index-test/c-index-test.c
    cfe/trunk/tools/clang-cc/clang-cc.cpp

Modified: cfe/trunk/include/clang-c/Index.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang-c/Index.h?rev=86306&r1=86305&r2=86306&view=diff

==============================================================================
--- cfe/trunk/include/clang-c/Index.h (original)
+++ cfe/trunk/include/clang-c/Index.h Fri Nov  6 18:00:49 2009
@@ -305,6 +305,327 @@
  */
 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

Modified: cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h?rev=86306&r1=86305&r2=86306&view=diff

==============================================================================
--- cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h (original)
+++ cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h Fri Nov  6 18:00:49 2009
@@ -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 @@
   /// \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 @@
     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 @@
     
     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 @@
     
     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 @@
     /// \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 @@
   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 @@
   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 @@
                                                 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 @@
 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, 

Modified: cfe/trunk/lib/Sema/CodeCompleteConsumer.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/CodeCompleteConsumer.cpp?rev=86306&r1=86305&r2=86306&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/CodeCompleteConsumer.cpp (original)
+++ cfe/trunk/lib/Sema/CodeCompleteConsumer.cpp Fri Nov  6 18:00:49 2009
@@ -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 @@
 //===----------------------------------------------------------------------===//
 // 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 @@
   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 @@
     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 @@
                           
   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 @@
   // 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();
+}

Modified: cfe/trunk/lib/Sema/SemaCodeComplete.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCodeComplete.cpp?rev=86306&r1=86305&r2=86306&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaCodeComplete.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCodeComplete.cpp Fri Nov  6 18:00:49 2009
@@ -187,8 +187,7 @@
                                      Context.getTypeDeclType(TD).getTypePtr());
     else
       assert(Parent->isTranslationUnit());
-  }
-  
+  }  
   return Result;
 }
 
@@ -674,6 +673,8 @@
 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 @@
     }
     
     if (P != 0)
-      CCStr->AddTextChunk(", ");
+      CCStr->AddChunk(Chunk(CodeCompletionString::CK_Comma));
     
     // Format the placeholder string.
     std::string PlaceholderStr;
@@ -713,6 +714,8 @@
                                        TemplateDecl *Template,
                                        CodeCompletionString *Result,
                                        unsigned MaxParameters = 0) {
+  typedef CodeCompletionString::Chunk Chunk;
+  
   CodeCompletionString *CCStr = Result;
   bool FirstParameter = true;
   
@@ -768,7 +771,7 @@
     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 @@
 /// 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 @@
     
     // 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 @@
         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 @@
     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 @@
         else {
           assert(isa<TemplateTemplateParmDecl>(Param));
           HasDefaultArg 
-          = cast<TemplateTemplateParmDecl>(Param)->hasDefaultArgument();
+            = cast<TemplateTemplateParmDecl>(Param)->hasDefaultArgument();
         }
         
         if (!HasDefaultArg)
@@ -896,16 +908,16 @@
       // 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 @@
     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 @@
 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 @@
     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 @@
     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 @@
     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 @@
   };
 }
 
-// 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 @@
   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 @@
     }
     
     // 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 @@
                                     NextRank, CurContext, Results);
   }
   
-  AddMacroResults(PP, NextRank, Results);
+  if (CodeCompleter->includeMacros())
+    AddMacroResults(PP, NextRank, Results);
   HandleCodeCompleteResults(CodeCompleter, Results.data(), Results.size());
 }
 
@@ -1255,7 +1271,8 @@
   }
   Results.ExitScope();
   
-  AddMacroResults(PP, 1, Results);
+  if (CodeCompleter->includeMacros())
+    AddMacroResults(PP, 1, Results);
   HandleCodeCompleteResults(CodeCompleter, Results.data(), Results.size());  
 }
 
@@ -1350,7 +1367,8 @@
   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 @@
                                            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 @@
   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 @@
     Results.ExitScope();
   }
   
-  AddMacroResults(PP, 1, Results);
+  if (CodeCompleter->includeMacros())
+    AddMacroResults(PP, 1, Results);
   HandleCodeCompleteResults(CodeCompleter, Results.data(), Results.size());  
 }
 
@@ -1433,7 +1454,8 @@
   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 @@
                                   NextRank + 1, CurContext, Results);
   Results.ExitScope();
   
-  AddMacroResults(PP, NextRank, Results);
+  if (CodeCompleter->includeMacros())
+    AddMacroResults(PP, NextRank, Results);
   HandleCodeCompleteResults(CodeCompleter, Results.data(), Results.size());  
 }
 

Modified: cfe/trunk/test/CodeCompletion/macros.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeCompletion/macros.c?rev=86306&r1=86305&r2=86306&view=diff

==============================================================================
--- cfe/trunk/test/CodeCompletion/macros.c (original)
+++ cfe/trunk/test/CodeCompletion/macros.c Fri Nov  6 18:00:49 2009
@@ -13,9 +13,9 @@
 };
 
 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

Modified: cfe/trunk/tools/CIndex/CIndex.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/CIndex/CIndex.cpp?rev=86306&r1=86305&r2=86306&view=diff

==============================================================================
--- cfe/trunk/tools/CIndex/CIndex.cpp (original)
+++ cfe/trunk/tools/CIndex/CIndex.cpp Fri Nov  6 18:00:49 2009
@@ -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 @@
   *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"

Modified: cfe/trunk/tools/CIndex/CIndex.exports
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/CIndex/CIndex.exports?rev=86306&r1=86305&r2=86306&view=diff

==============================================================================
--- cfe/trunk/tools/CIndex/CIndex.exports (original)
+++ cfe/trunk/tools/CIndex/CIndex.exports Fri Nov  6 18:00:49 2009
@@ -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_getEntityFromDecl
 _clang_getFileName
 _clang_getFileTime
+_clang_getNumCompletionChunks
 _clang_getTranslationUnitSpelling
 _clang_getURI
 _clang_isDeclaration

Modified: cfe/trunk/tools/c-index-test/c-index-test.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/c-index-test/c-index-test.c?rev=86306&r1=86305&r2=86306&view=diff

==============================================================================
--- cfe/trunk/tools/c-index-test/c-index-test.c (original)
+++ cfe/trunk/tools/c-index-test/c-index-test.c Fri Nov  6 18:00:49 2009
@@ -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 @@
   }
 }
 
+/* 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;

Modified: cfe/trunk/tools/clang-cc/clang-cc.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/clang-cc/clang-cc.cpp?rev=86306&r1=86305&r2=86306&view=diff

==============================================================================
--- cfe/trunk/tools/clang-cc/clang-cc.cpp (original)
+++ cfe/trunk/tools/clang-cc/clang-cc.cpp Fri Nov  6 18:00:49 2009
@@ -215,15 +215,45 @@
  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;
 }
 
 //===----------------------------------------------------------------------===//





More information about the cfe-commits mailing list