Add code-completion support directly to ASTUnit, which performs code
completion within the translation unit using the same command-line
arguments for parsing the translation unit. Eventually, we'll reuse
the precompiled preamble to improve code-completion performance, and
this also gives us a place to cache results.
Expose this function via the new libclang function
clang_codeCompleteAt(), which performs the code completion within a
CXTranslationUnit. The completion occurs in-process
(clang_codeCompletion() runs code completion out-of-process).
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@110210 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c
index 795c19c..8146213 100644
--- a/tools/c-index-test/c-index-test.c
+++ b/tools/c-index-test/c-index-test.c
@@ -888,11 +888,21 @@
return -1;
CIdx = clang_createIndex(0, 1);
- results = clang_codeComplete(CIdx,
- argv[argc - 1], argc - num_unsaved_files - 3,
- argv + num_unsaved_files + 2,
- num_unsaved_files, unsaved_files,
- filename, line, column);
+ if (getenv("CINDEXTEST_EDITING")) {
+ CXTranslationUnit *TU = clang_parseTranslationUnit(CIdx, 0,
+ argv + num_unsaved_files + 2,
+ argc - num_unsaved_files - 2,
+ unsaved_files,
+ num_unsaved_files,
+ getDefaultParsingOptions());
+ results = clang_codeCompleteAt(TU, filename, line, column,
+ unsaved_files, num_unsaved_files);
+ } else
+ results = clang_codeComplete(CIdx,
+ argv[argc - 1], argc - num_unsaved_files - 3,
+ argv + num_unsaved_files + 2,
+ num_unsaved_files, unsaved_files,
+ filename, line, column);
if (results) {
unsigned i, n = results->NumResults;
diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp
index b90740f..6414166 100644
--- a/tools/libclang/CIndex.cpp
+++ b/tools/libclang/CIndex.cpp
@@ -1443,7 +1443,7 @@
for (unsigned I = 0; I != num_unsaved_files; ++I) {
llvm::StringRef Data(unsaved_files[I].Contents, unsaved_files[I].Length);
const llvm::MemoryBuffer *Buffer
- = llvm::MemoryBuffer::getMemBufferCopy(Data, unsaved_files[I].Filename);
+ = llvm::MemoryBuffer::getMemBufferCopy(Data, unsaved_files[I].Filename);
RemappedFiles.push_back(std::make_pair(unsaved_files[I].Filename,
Buffer));
}
diff --git a/tools/libclang/CIndexCodeCompletion.cpp b/tools/libclang/CIndexCodeCompletion.cpp
index 2df5241..f8fd470 100644
--- a/tools/libclang/CIndexCodeCompletion.cpp
+++ b/tools/libclang/CIndexCodeCompletion.cpp
@@ -16,6 +16,7 @@
#include "CIndexDiagnostic.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/FileManager.h"
+#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Sema/CodeCompleteConsumer.h"
@@ -231,7 +232,7 @@
llvm::SmallVector<StoredDiagnostic, 8> Diagnostics;
/// \brief Diag object
- Diagnostic Diag;
+ llvm::IntrusiveRefCntPtr<Diagnostic> Diag;
/// \brief Language options used to adjust source locations.
LangOptions LangOpts;
@@ -248,7 +249,8 @@
};
AllocatedCXCodeCompleteResults::AllocatedCXCodeCompleteResults()
- : CXCodeCompleteResults(), Buffer(0), SourceMgr(Diag) { }
+ : CXCodeCompleteResults(), Buffer(0), Diag(new Diagnostic),
+ SourceMgr(*Diag) { }
AllocatedCXCodeCompleteResults::~AllocatedCXCodeCompleteResults() {
for (unsigned I = 0, N = NumResults; I != N; ++I)
@@ -529,6 +531,165 @@
return Results;
}
+} // end extern "C"
+
+namespace clang {
+ // FIXME: defined in CodeCompleteConsumer.cpp, but should be a
+ // static function here.
+ CXCursorKind
+ getCursorKindForCompletionResult(const CodeCompleteConsumer::Result &R);
+}
+
+
+namespace {
+ class CaptureCompletionResults : public CodeCompleteConsumer {
+ AllocatedCXCodeCompleteResults &AllocatedResults;
+
+ public:
+ explicit CaptureCompletionResults(AllocatedCXCodeCompleteResults &Results)
+ : CodeCompleteConsumer(true, false, false), AllocatedResults(Results) { }
+
+ virtual void ProcessCodeCompleteResults(Sema &S, Result *Results,
+ unsigned NumResults) {
+ AllocatedResults.Results = new CXCompletionResult [NumResults];
+ AllocatedResults.NumResults = NumResults;
+ for (unsigned I = 0; I != NumResults; ++I) {
+ CXStoredCodeCompletionString *StoredCompletion
+ = new CXStoredCodeCompletionString(Results[I].Priority);
+ (void)Results[I].CreateCodeCompletionString(S, StoredCompletion);
+ AllocatedResults.Results[I].CursorKind
+ = getCursorKindForCompletionResult(Results[I]);
+ AllocatedResults.Results[I].CompletionString = StoredCompletion;
+ }
+ }
+ };
+}
+
+extern "C" {
+CXCodeCompleteResults *clang_codeCompleteAt(CXTranslationUnit TU,
+ const char *complete_filename,
+ unsigned complete_line,
+ unsigned complete_column,
+ struct CXUnsavedFile *unsaved_files,
+ unsigned num_unsaved_files) {
+#ifdef UDP_CODE_COMPLETION_LOGGER
+#ifdef UDP_CODE_COMPLETION_LOGGER_PORT
+ const llvm::TimeRecord &StartTime = llvm::TimeRecord::getCurrentTime();
+#endif
+#endif
+
+ bool EnableLogging = getenv("LIBCLANG_CODE_COMPLETION_LOGGING") != 0;
+
+ ASTUnit *AST = static_cast<ASTUnit *>(TU);
+ if (!AST)
+ return 0;
+
+ // Perform the remapping of source files.
+ llvm::SmallVector<ASTUnit::RemappedFile, 4> RemappedFiles;
+ for (unsigned I = 0; I != num_unsaved_files; ++I) {
+ llvm::StringRef Data(unsaved_files[I].Contents, unsaved_files[I].Length);
+ const llvm::MemoryBuffer *Buffer
+ = llvm::MemoryBuffer::getMemBufferCopy(Data, unsaved_files[I].Filename);
+ RemappedFiles.push_back(std::make_pair(unsaved_files[I].Filename,
+ Buffer));
+ }
+
+ if (EnableLogging) {
+ // FIXME: Add logging.
+ }
+
+ // Parse the resulting source file to find code-completion results.
+ AllocatedCXCodeCompleteResults *Results = new AllocatedCXCodeCompleteResults;
+ Results->Results = 0;
+ Results->NumResults = 0;
+ Results->Buffer = 0;
+
+ // Create a code-completion consumer to capture the results.
+ CaptureCompletionResults Capture(*Results);
+
+ // Perform completion.
+ AST->CodeComplete(complete_filename, complete_line, complete_column,
+ RemappedFiles.data(), RemappedFiles.size(), Capture,
+ *Results->Diag, Results->LangOpts, Results->SourceMgr,
+ Results->FileMgr, Results->Diagnostics);
+
+
+
+#ifdef UDP_CODE_COMPLETION_LOGGER
+#ifdef UDP_CODE_COMPLETION_LOGGER_PORT
+ const llvm::TimeRecord &EndTime = llvm::TimeRecord::getCurrentTime();
+ llvm::SmallString<256> LogResult;
+ llvm::raw_svector_ostream os(LogResult);
+
+ // Figure out the language and whether or not it uses PCH.
+ const char *lang = 0;
+ bool usesPCH = false;
+
+ for (std::vector<const char*>::iterator I = argv.begin(), E = argv.end();
+ I != E; ++I) {
+ if (*I == 0)
+ continue;
+ if (strcmp(*I, "-x") == 0) {
+ if (I + 1 != E) {
+ lang = *(++I);
+ continue;
+ }
+ }
+ else if (strcmp(*I, "-include") == 0) {
+ if (I+1 != E) {
+ const char *arg = *(++I);
+ llvm::SmallString<512> pchName;
+ {
+ llvm::raw_svector_ostream os(pchName);
+ os << arg << ".pth";
+ }
+ pchName.push_back('\0');
+ struct stat stat_results;
+ if (stat(pchName.data(), &stat_results) == 0)
+ usesPCH = true;
+ continue;
+ }
+ }
+ }
+
+ os << "{ ";
+ os << "\"wall\": " << (EndTime.getWallTime() - StartTime.getWallTime());
+ os << ", \"numRes\": " << Results->NumResults;
+ os << ", \"diags\": " << Results->Diagnostics.size();
+ os << ", \"pch\": " << (usesPCH ? "true" : "false");
+ os << ", \"lang\": \"" << (lang ? lang : "<unknown>") << '"';
+ const char *name = getlogin();
+ os << ", \"user\": \"" << (name ? name : "unknown") << '"';
+ os << ", \"clangVer\": \"" << getClangFullVersion() << '"';
+ os << " }";
+
+ llvm::StringRef res = os.str();
+ if (res.size() > 0) {
+ do {
+ // Setup the UDP socket.
+ struct sockaddr_in servaddr;
+ bzero(&servaddr, sizeof(servaddr));
+ servaddr.sin_family = AF_INET;
+ servaddr.sin_port = htons(UDP_CODE_COMPLETION_LOGGER_PORT);
+ if (inet_pton(AF_INET, UDP_CODE_COMPLETION_LOGGER,
+ &servaddr.sin_addr) <= 0)
+ break;
+
+ int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0)
+ break;
+
+ sendto(sockfd, res.data(), res.size(), 0,
+ (struct sockaddr *)&servaddr, sizeof(servaddr));
+ close(sockfd);
+ }
+ while (false);
+ }
+#endif
+#endif
+ return Results;
+}
+
void clang_disposeCodeCompleteResults(CXCodeCompleteResults *ResultsIn) {
if (!ResultsIn)
return;
diff --git a/tools/libclang/libclang.darwin.exports b/tools/libclang/libclang.darwin.exports
index 50ed994..b971ed3 100644
--- a/tools/libclang/libclang.darwin.exports
+++ b/tools/libclang/libclang.darwin.exports
@@ -1,6 +1,7 @@
_clang_CXXMethod_isStatic
_clang_annotateTokens
_clang_codeComplete
+_clang_codeCompleteAt
_clang_codeCompleteGetDiagnostic
_clang_codeCompleteGetNumDiagnostics
_clang_constructUSR_ObjCCategory
diff --git a/tools/libclang/libclang.exports b/tools/libclang/libclang.exports
index 752c650..b5533fc 100644
--- a/tools/libclang/libclang.exports
+++ b/tools/libclang/libclang.exports
@@ -1,6 +1,7 @@
clang_CXXMethod_isStatic
clang_annotateTokens
clang_codeComplete
+clang_codeCompleteAt
clang_codeCompleteGetDiagnostic
clang_codeCompleteGetNumDiagnostics
clang_constructUSR_ObjCCategory