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.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@86306 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/tools/CIndex/CIndex.cpp b/tools/CIndex/CIndex.cpp
index 4798e28..9389139 100644
--- a/tools/CIndex/CIndex.cpp
+++ b/tools/CIndex/CIndex.cpp
@@ -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"