diff options
-rw-r--r-- | include/clang-c/Index.h | 37 | ||||
-rw-r--r-- | test/Index/Inputs/remap-complete-to.c | 1 | ||||
-rw-r--r-- | test/Index/remap-complete.c | 4 | ||||
-rw-r--r-- | tools/CIndex/CIndex.cpp | 43 | ||||
-rw-r--r-- | tools/c-index-test/c-index-test.c | 96 |
5 files changed, 179 insertions, 2 deletions
diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h index 85f7a6a31b..8e6eeb5422 100644 --- a/include/clang-c/Index.h +++ b/include/clang-c/Index.h @@ -104,6 +104,34 @@ enum CXCursorKind { CXCursor_LastInvalid = 72 }; +/** + * \brief Provides the contents of a file that has not yet been saved to disk. + * + * Each CXUnsavedFile instance provides the name of a file on the + * system along with the current contents of that file that have not + * yet been saved to disk. + */ +struct CXUnsavedFile { + /** + * \brief The file whose contents have not yet been saved. + * + * This file must already exist in the file system. + */ + const char *Filename; + + /** + * \brief A null-terminated buffer containing the unsaved contents + * of this file. + */ + const char *Contents; + + /** + * \brief The length of the unsaved contents of this buffer, not + * counting the NULL at the end of the buffer. + */ + unsigned long Length; +}; + /* A cursor into the CXTranslationUnit. */ typedef struct { @@ -621,6 +649,13 @@ clang_getNumCompletionChunks(CXCompletionString completion_string); * includes, etc., but should not include any information specific to * code completion. * + * \param num_unsaved_files the number of unsaved file entries in \p + * unsaved_files. + * + * \param unsaved_files the files that have not yet been saved to disk + * but may be required for code completion, including the contents of + * those files. + * * \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 @@ -643,6 +678,8 @@ 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, diff --git a/test/Index/Inputs/remap-complete-to.c b/test/Index/Inputs/remap-complete-to.c new file mode 100644 index 0000000000..9f8be2cbec --- /dev/null +++ b/test/Index/Inputs/remap-complete-to.c @@ -0,0 +1 @@ +void f0() { } diff --git a/test/Index/remap-complete.c b/test/Index/remap-complete.c new file mode 100644 index 0000000000..252108b404 --- /dev/null +++ b/test/Index/remap-complete.c @@ -0,0 +1,4 @@ +// RUN: c-index-test -code-completion-at=%s:1:12 -remap-file="%s;%S/Inputs/remap-complete-to.c" %s | FileCheck %s + +// CHECK: FunctionDecl:{TypedText f0}{LeftParen (}{RightParen )} +void f() { } diff --git a/tools/CIndex/CIndex.cpp b/tools/CIndex/CIndex.cpp index 4681b939ce..87d7856203 100644 --- a/tools/CIndex/CIndex.cpp +++ b/tools/CIndex/CIndex.cpp @@ -1141,6 +1141,8 @@ 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, @@ -1149,6 +1151,9 @@ void clang_codeComplete(CXIndex CIdx, // The indexer, which is mainly used to determine where diagnostics go. CIndexer *CXXIdx = static_cast<CIndexer *>(CIdx); + // The set of temporary files that we've built. + std::vector<llvm::sys::Path> TemporaryFiles; + // Build up the arguments for invoking 'clang'. std::vector<const char *> argv; @@ -1174,6 +1179,40 @@ void clang_codeComplete(CXIndex CIdx, argv.push_back("-Xclang"); argv.push_back("-no-code-completion-debug-printer"); + std::vector<std::string> RemapArgs; + for (unsigned i = 0; i != num_unsaved_files; ++i) { + char tmpFile[L_tmpnam]; + char *tmpFileName = tmpnam(tmpFile); + + // Write the contents of this unsaved file into the temporary file. + llvm::sys::Path SavedFile(tmpFileName); + std::string ErrorInfo; + llvm::raw_fd_ostream OS(SavedFile.c_str(), ErrorInfo); + if (!ErrorInfo.empty()) + continue; + + OS.write(unsaved_files[i].Contents, unsaved_files[i].Length); + OS.close(); + if (OS.has_error()) { + SavedFile.eraseFromDisk(); + continue; + } + + // Remap the file. + std::string RemapArg = "-remap-file="; + RemapArg += unsaved_files[i].Filename; + RemapArg += ';'; + RemapArg += tmpFileName; + RemapArgs.push_back("-Xclang"); + RemapArgs.push_back(RemapArg); + TemporaryFiles.push_back(SavedFile); + } + + // The pointers into the elements of RemapArgs are stable because we + // won't be adding anything to RemapArgs after this point. + for (unsigned i = 0, e = RemapArgs.size(); i != e; ++i) + argv.push_back(RemapArgs[i].c_str()); + // 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). if (source_filename) @@ -1203,6 +1242,7 @@ void clang_codeComplete(CXIndex CIdx, char tmpFile[L_tmpnam]; char *tmpFileName = tmpnam(tmpFile); llvm::sys::Path ResultsFile(tmpFileName); + TemporaryFiles.push_back(ResultsFile); // Invoke 'clang'. llvm::sys::Path DevNull; // leave empty, causes redirection to /dev/null @@ -1255,7 +1295,8 @@ void clang_codeComplete(CXIndex CIdx, delete F; } - ResultsFile.eraseFromDisk(); + for (unsigned i = 0, e = TemporaryFiles.size(); i != e; ++i) + TemporaryFiles[i].eraseFromDisk(); } } // end extern "C" diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c index 5364d7948c..efff1ae318 100644 --- a/tools/c-index-test/c-index-test.c +++ b/tools/c-index-test/c-index-test.c @@ -389,6 +389,91 @@ void print_completion_result(CXCompletionResult *completion_result, fprintf(file, "\n"); } +void free_remapped_files(struct CXUnsavedFile *unsaved_files, + int num_unsaved_files) { + int i; + for (i = 0; i != num_unsaved_files; ++i) { + free((char *)unsaved_files[i].Filename); + free((char *)unsaved_files[i].Contents); + } +} + +int parse_remapped_files(int argc, const char **argv, int start_arg, + struct CXUnsavedFile **unsaved_files, + int *num_unsaved_files) { + int i; + int arg; + int prefix_len = strlen("-remap-file="); + *unsaved_files = 0; + *num_unsaved_files = 0; + + /* Count the number of remapped files. */ + for (arg = start_arg; arg < argc; ++arg) { + if (strncmp(argv[arg], "-remap-file=", prefix_len)) + break; + + ++*num_unsaved_files; + } + + if (*num_unsaved_files == 0) + return 0; + + *unsaved_files + = (struct CXUnsavedFile *)malloc(sizeof(struct CXUnsavedFile) * + *num_unsaved_files); + for (arg = start_arg, i = 0; i != *num_unsaved_files; ++i, ++arg) { + struct CXUnsavedFile *unsaved = *unsaved_files + i; + const char *arg_string = argv[arg] + prefix_len; + int filename_len; + char *filename; + char *contents; + FILE *to_file; + const char *semi = strchr(arg_string, ';'); + if (!semi) { + fprintf(stderr, + "error: -remap-file=from;to argument is missing semicolon\n"); + free_remapped_files(*unsaved_files, i); + *unsaved_files = 0; + *num_unsaved_files = 0; + return -1; + } + + /* Open the file that we're remapping to. */ + to_file = fopen(semi + 1, "r"); + if (!to_file) { + fprintf(stderr, "error: cannot open file %s that we are remapping to\n", + semi + 1); + free_remapped_files(*unsaved_files, i); + *unsaved_files = 0; + *num_unsaved_files = 0; + return -1; + } + + /* Determine the length of the file we're remapping to. */ + fseek(to_file, 0, SEEK_END); + unsaved->Length = ftell(to_file); + fseek(to_file, 0, SEEK_SET); + + /* Read the contents of the file we're remapping to. */ + contents = (char *)malloc(unsaved->Length + 1); + fread(contents, 1, unsaved->Length, to_file); + contents[unsaved->Length] = 0; + unsaved->Contents = contents; + + /* Close the file. */ + fclose(to_file); + + /* Copy the file name that we're remapping from. */ + filename_len = semi - arg_string; + filename = (char *)malloc(filename_len + 1); + memcpy(filename, arg_string, filename_len); + filename[filename_len] = 0; + unsaved->Filename = filename; + } + + return 0; +} + int perform_code_completion(int argc, const char **argv) { const char *input = argv[1]; char *filename = 0; @@ -396,17 +481,26 @@ int perform_code_completion(int argc, const char **argv) { unsigned column; CXIndex CIdx; int errorCode; + struct CXUnsavedFile *unsaved_files = 0; + int num_unsaved_files = 0; input += strlen("-code-completion-at="); if ((errorCode = parse_file_line_column(input, &filename, &line, &column))) return errorCode; + if (parse_remapped_files(argc, argv, 2, &unsaved_files, &num_unsaved_files)) + return -1; + CIdx = clang_createIndex(0, 0); - clang_codeComplete(CIdx, argv[argc - 1], argc - 3, argv + 2, + 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); clang_disposeIndex(CIdx); free(filename); + free_remapped_files(unsaved_files, num_unsaved_files); + return 0; } |