From ec6762c709726bf2ee5f76c21df81e71a56e6f81 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Fri, 18 Dec 2009 16:20:58 +0000 Subject: [PATCH] Change clang_codeComplete API to return the results in a structure on the heap, so that clients are not forced to copy the results during the initial iteration. A separate clang_disposeCodeCompleteResults function frees the returned results. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@91690 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang-c/Index.h | 76 +++++++++++++++++-------------- tools/CIndex/CIndex.cpp | 64 +++++++++++++++++++------- tools/CIndex/CIndex.exports | 1 + tools/c-index-test/c-index-test.c | 17 +++++-- 4 files changed, 104 insertions(+), 54 deletions(-) diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h index 99ea1b74e8..574ea2403c 100644 --- a/include/clang-c/Index.h +++ b/include/clang-c/Index.h @@ -558,21 +558,6 @@ enum CXCompletionChunkKind { */ 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. @@ -623,6 +608,26 @@ clang_getCompletionChunkCompletionString(CXCompletionString completion_string, CINDEX_LINKAGE unsigned clang_getNumCompletionChunks(CXCompletionString completion_string); +/** + * \brief Contains the results of code-completion. + * + * This data structure contains the results of code completion, as + * produced by \c clang_codeComplete. Its contents must be freed by + * \c clang_disposeCodeCompleteResults. + */ +typedef struct { + /** + * \brief The code-completion results. + */ + CXCompletionResult *Results; + + /** + * \brief The number of code-completion results stored in the + * \c Results array. + */ + unsigned NumResults; +} CXCodeCompleteResults; + /** * \brief Perform code completion at a given location in a source file. * @@ -635,7 +640,7 @@ clang_getNumCompletionChunks(CXCompletionString completion_string); * 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. + * returned via a new \c CXCodeCompleteResults structure. * * Code completion itself is meant to be triggered by the client when the * user types punctuation characters or whitespace, at which point the @@ -650,7 +655,7 @@ clang_getNumCompletionChunks(CXCompletionString completion_string); * 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 + * is to separate the relatively high-latency acquisition of code-completion * results from the filtering of results on a per-character basis, which must * have a lower latency. * @@ -691,24 +696,27 @@ clang_getNumCompletionChunks(CXCompletionString completion_string); * 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. + * \returns if successful, a new CXCodeCompleteResults structure + * containing code-completion results, which should eventually be + * freed with \c clang_disposeCodeCompleteResults(). If code + * completion fails, returns NULL. + */ +CINDEX_LINKAGE +CXCodeCompleteResults *clang_codeComplete(CXIndex CIdx, + const char *source_filename, + int num_command_line_args, + const char **command_line_args, + unsigned num_unsaved_files, + struct CXUnsavedFile *unsaved_files, + const char *complete_filename, + unsigned complete_line, + unsigned complete_column); + +/** + * \brief Free the given set of code-completion results. */ -CINDEX_LINKAGE void clang_codeComplete(CXIndex CIdx, - const char *source_filename, - int num_command_line_args, - const char **command_line_args, - unsigned num_unsaved_files, - struct CXUnsavedFile *unsaved_files, - const char *complete_filename, - unsigned complete_line, - unsigned complete_column, - CXCompletionIterator completion_iterator, - CXClientData client_data); - +CINDEX_LINKAGE +void clang_disposeCodeCompleteResults(CXCodeCompleteResults *Results); #ifdef __cplusplus } diff --git a/tools/CIndex/CIndex.cpp b/tools/CIndex/CIndex.cpp index fe3aa37694..c4a616689a 100644 --- a/tools/CIndex/CIndex.cpp +++ b/tools/CIndex/CIndex.cpp @@ -1220,17 +1220,23 @@ static bool ReadUnsigned(const char *&Memory, const char *MemoryEnd, return false; } -void clang_codeComplete(CXIndex CIdx, - const char *source_filename, - int num_command_line_args, - const char **command_line_args, - unsigned num_unsaved_files, - struct CXUnsavedFile *unsaved_files, - const char *complete_filename, - unsigned complete_line, - unsigned complete_column, - CXCompletionIterator completion_iterator, - CXClientData client_data) { +/// \brief The CXCodeCompleteResults structure we allocate internally; +/// the client only sees the initial CXCodeCompleteResults structure. +struct AllocatedCXCodeCompleteResults : public CXCodeCompleteResults { + /// \brief The memory buffer from which we parsed the results. We + /// retain this buffer because the completion strings point into it. + llvm::MemoryBuffer *Buffer; +}; + +CXCodeCompleteResults *clang_codeComplete(CXIndex CIdx, + const char *source_filename, + int num_command_line_args, + const char **command_line_args, + unsigned num_unsaved_files, + struct CXUnsavedFile *unsaved_files, + const char *complete_filename, + unsigned complete_line, + unsigned complete_column) { // The indexer, which is mainly used to determine where diagnostics go. CIndexer *CXXIdx = static_cast(CIdx); @@ -1353,7 +1359,9 @@ void clang_codeComplete(CXIndex CIdx, // Parse the resulting source file to find code-completion results. using llvm::MemoryBuffer; using llvm::StringRef; + AllocatedCXCodeCompleteResults *Results = 0; if (MemoryBuffer *F = MemoryBuffer::getFile(ResultsFile.c_str())) { + llvm::SmallVector CompletionResults; StringRef Buffer = F->getBuffer(); for (const char *Str = Buffer.data(), *StrEnd = Str + Buffer.size(); Str < StrEnd;) { @@ -1371,17 +1379,41 @@ void clang_codeComplete(CXIndex CIdx, CXCompletionResult Result; Result.CursorKind = (CXCursorKind)KindValue; Result.CompletionString = CCStr; - if (completion_iterator) - completion_iterator(&Result, client_data); + CompletionResults.push_back(Result); } - - delete CCStr; }; - delete F; + + // Allocate the results. + Results = new AllocatedCXCodeCompleteResults; + Results->Results = new CXCompletionResult [CompletionResults.size()]; + Results->NumResults = CompletionResults.size(); + memcpy(Results->Results, CompletionResults.data(), + CompletionResults.size() * sizeof(CXCompletionResult)); + Results->Buffer = F; } for (unsigned i = 0, e = TemporaryFiles.size(); i != e; ++i) TemporaryFiles[i].eraseFromDisk(); + + return Results; +} + +void clang_disposeCodeCompleteResults(CXCodeCompleteResults *ResultsIn) { + if (!ResultsIn) + return; + + AllocatedCXCodeCompleteResults *Results + = static_cast(ResultsIn); + + for (unsigned I = 0, N = Results->NumResults; I != N; ++I) + delete (CXCompletionString *)Results->Results[I].CompletionString; + delete [] Results->Results; + + Results->Results = 0; + Results->NumResults = 0; + delete Results->Buffer; + Results->Buffer = 0; + delete Results; } } // end extern "C" diff --git a/tools/CIndex/CIndex.exports b/tools/CIndex/CIndex.exports index 458ad10283..e925df945d 100644 --- a/tools/CIndex/CIndex.exports +++ b/tools/CIndex/CIndex.exports @@ -2,6 +2,7 @@ _clang_codeComplete _clang_createIndex _clang_createTranslationUnit _clang_createTranslationUnitFromSourceFile +_clang_disposeCodeCompleteResults _clang_disposeIndex _clang_disposeString _clang_disposeTranslationUnit diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c index 7040a7288a..f3458adda8 100644 --- a/tools/c-index-test/c-index-test.c +++ b/tools/c-index-test/c-index-test.c @@ -496,6 +496,7 @@ int perform_code_completion(int argc, const char **argv) { int errorCode; struct CXUnsavedFile *unsaved_files = 0; int num_unsaved_files = 0; + CXCodeCompleteResults *results = 0; input += strlen("-code-completion-at="); if ((errorCode = parse_file_line_column(input, &filename, &line, &column))) @@ -505,10 +506,18 @@ int perform_code_completion(int argc, const char **argv) { return -1; CIdx = clang_createIndex(0, 0); - clang_codeComplete(CIdx, argv[argc - 1], argc - num_unsaved_files - 3, - argv + num_unsaved_files + 2, - num_unsaved_files, unsaved_files, - filename, line, column, &print_completion_result, stdout); + 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; + for (i = 0; i != n; ++i) + print_completion_result(results->Results + i, stdout); + clang_disposeCodeCompleteResults(results); + } + clang_disposeIndex(CIdx); free(filename); -- 2.50.1