* \p command_line_args.
*
* \param unsaved_files the files that have not yet been saved to disk
- * but may be required for code completion, including the contents of
+ * but may be required for parsing, including the contents of
* those files. The contents and name of these files (as specified by
* CXUnsavedFile) are copied when necessary, so the client only needs to
* guarantee their validity until the call to this function returns.
unsigned complete_line,
unsigned complete_column);
+/**
+ * \brief Perform code completion at a given location in a translation unit.
+ *
+ * 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
+ * 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
+ * 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-completion
+ * results from the filtering of results on a per-character basis, which must
+ * have a lower latency.
+ *
+ * \param TU The translation unit in which code-completion should
+ * occur. The source files for this translation unit need not be
+ * completely up-to-date (and the contents of those source files may
+ * be overridden via \p unsaved_files). Cursors referring into the
+ * translation unit may be invalidated by this invocation.
+ *
+ * \param complete_filename The name of the source file where code
+ * completion should be performed. This filename may be any file
+ * included in the translation unit.
+ *
+ * \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 unsaved_files the Tiles that have not yet been saved to disk
+ * but may be required for parsing or code completion, including the
+ * contents of those files. The contents and name of these files (as
+ * specified by CXUnsavedFile) are copied when necessary, so the
+ * client only needs to guarantee their validity until the call to
+ * this function returns.
+ *
+ * \param num_unsaved_files The number of unsaved file entries in \p
+ * unsaved_files.
+ *
+ * \returns If successful, a new \c 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_codeCompleteAt(CXTranslationUnit TU,
+ const char *complete_filename,
+ unsigned complete_line,
+ unsigned complete_column,
+ struct CXUnsavedFile *unsaved_files,
+ unsigned num_unsaved_files);
+
/**
* \brief Free the given set of code-completion results.
*/
namespace clang {
class ASTContext;
+class CodeCompleteConsumer;
class CompilerInvocation;
class Decl;
class Diagnostic;
/// contain any translation-unit information, false otherwise.
bool Reparse(RemappedFile *RemappedFiles = 0,
unsigned NumRemappedFiles = 0);
+
+ /// \brief Perform code completion at the given file, line, and
+ /// column within this translation unit.
+ ///
+ /// \brief File The file in which code completion will occur.
+ /// \brief Line The line at which code completion will occur.
+ /// \brief Column The column at which code completion will occur.
+ /// \brief Consumer The consumer that will receive code-completion results.
+ ///
+ /// FIXME: The Diag, LangOpts, SourceMgr, FileMgr, and
+ /// StoredDiagnostics parameters are all disgusting hacks. They will
+ /// go away.
+ void CodeComplete(llvm::StringRef File, unsigned Line, unsigned Column,
+ RemappedFile *RemappedFiles, unsigned NumRemappedFiles,
+ CodeCompleteConsumer &Consumer,
+ Diagnostic &Diag, LangOptions &LangOpts,
+ SourceManager &SourceMgr, FileManager &FileMgr,
+ llvm::SmallVectorImpl<StoredDiagnostic> &StoredDiagnostics);
};
} // namespace clang
std::string getAsString() const;
/// \brief Clone this code-completion string.
- CodeCompletionString *Clone() const;
+ ///
+ /// \param Result If non-NULL, points to an empty code-completion
+ /// result that will be given a cloned copy of
+ CodeCompletionString *Clone(CodeCompletionString *Result = 0) const;
/// \brief Serialize this code-completion string to the given stream.
void Serialize(llvm::raw_ostream &OS) const;
/// \brief Create a new code-completion string that describes how to insert
/// this result into a program.
- CodeCompletionString *CreateCodeCompletionString(Sema &S);
+ ///
+ /// \param S The semantic analysis that created the result.
+ ///
+ /// \param Result If non-NULL, the already-allocated, empty
+ /// code-completion string that will be populated with the
+ /// appropriate code completion string for this result.
+ CodeCompletionString *CreateCodeCompletionString(Sema &S,
+ CodeCompletionString *Result = 0);
void Destroy();
Clang.setInvocation(Invocation.take());
OriginalSourceFile = Clang.getFrontendOpts().Inputs[0].second;
- // Set up diagnostics.
+ // Set up diagnostics, capturing any diagnostics that would
+ // otherwise be dropped.
Clang.setDiagnostics(&getDiagnostics());
+ CaptureDroppedDiagnostics Capture(CaptureDiagnostics,
+ getDiagnostics(),
+ StoredDiagnostics);
Clang.setDiagnosticClient(getDiagnostics().getClient());
// Create the target instance.
if (!OverrideMainBuffer)
StoredDiagnostics.clear();
-
- // Capture any diagnostics that would otherwise be dropped.
- CaptureDroppedDiagnostics Capture(CaptureDiagnostics,
- Clang.getDiagnostics(),
- StoredDiagnostics);
-
+
// Create a file manager object to provide access to and cache the filesystem.
Clang.setFileManager(&getFileManager());
// If the main file has been overridden due to the use of a preamble,
// make that override happen and introduce the preamble.
PreprocessorOptions &PreprocessorOpts = Clang.getPreprocessorOpts();
+ std::string PriorImplicitPCHInclude;
if (OverrideMainBuffer) {
PreprocessorOpts.addRemappedFile(OriginalSourceFile, OverrideMainBuffer);
PreprocessorOpts.PrecompiledPreambleBytes.first = Preamble.size();
PreprocessorOpts.PrecompiledPreambleBytes.second
= PreambleEndsAtStartOfLine;
+ PriorImplicitPCHInclude = PreprocessorOpts.ImplicitPCHInclude;
PreprocessorOpts.ImplicitPCHInclude = PreambleFile;
PreprocessorOpts.DisablePCHValidation = true;
Act->EndSourceFile();
// Remove the overridden buffer we used for the preamble.
- if (OverrideMainBuffer)
+ if (OverrideMainBuffer) {
PreprocessorOpts.eraseRemappedFile(
PreprocessorOpts.remapped_file_buffer_end() - 1);
-
+ PreprocessorOpts.ImplicitPCHInclude = PriorImplicitPCHInclude;
+ }
+
Clang.takeDiagnosticClient();
Invocation.reset(Clang.takeInvocation());
PreprocessorOpts.eraseRemappedFile(
PreprocessorOpts.remapped_file_buffer_end() - 1);
PreprocessorOpts.DisablePCHValidation = true;
+ PreprocessorOpts.ImplicitPCHInclude = PriorImplicitPCHInclude;
}
Clang.takeSourceManager();
Clang.setInvocation(&PreambleInvocation);
OriginalSourceFile = Clang.getFrontendOpts().Inputs[0].second;
- // Set up diagnostics.
+ // Set up diagnostics, capturing all of the diagnostics produced.
Clang.setDiagnostics(&getDiagnostics());
+ CaptureDroppedDiagnostics Capture(CaptureDiagnostics,
+ getDiagnostics(),
+ StoredDiagnostics);
Clang.setDiagnosticClient(getDiagnostics().getClient());
// Create the target instance.
StoredDiagnostics.clear();
TopLevelDecls.clear();
TopLevelDeclsInPreamble.clear();
-
- // Capture any diagnostics that would otherwise be dropped.
- CaptureDroppedDiagnostics Capture(CaptureDiagnostics,
- getDiagnostics(),
- StoredDiagnostics);
// Create a file manager object to provide access to and cache the filesystem.
Clang.setFileManager(new FileManager);
}
// Remap files.
- // FIXME: Do we want to remove old mappings for these files?
Invocation->getPreprocessorOpts().clearRemappedFiles();
for (unsigned I = 0; I != NumRemappedFiles; ++I)
Invocation->getPreprocessorOpts().addRemappedFile(RemappedFiles[I].first,
ReparsingTimer->stopTimer();
return Result;
}
+
+void ASTUnit::CodeComplete(llvm::StringRef File, unsigned Line, unsigned Column,
+ RemappedFile *RemappedFiles,
+ unsigned NumRemappedFiles,
+ CodeCompleteConsumer &Consumer,
+ Diagnostic &Diag, LangOptions &LangOpts,
+ SourceManager &SourceMgr, FileManager &FileMgr,
+ llvm::SmallVectorImpl<StoredDiagnostic> &StoredDiagnostics) {
+ if (!Invocation.get())
+ return;
+
+ CompilerInvocation CCInvocation(*Invocation);
+ FrontendOptions &FrontendOpts = CCInvocation.getFrontendOpts();
+ PreprocessorOptions &PreprocessorOpts = CCInvocation.getPreprocessorOpts();
+
+ FrontendOpts.ShowMacrosInCodeCompletion = 1;
+ FrontendOpts.ShowCodePatternsInCodeCompletion = 1;
+ FrontendOpts.CodeCompletionAt.FileName = File;
+ FrontendOpts.CodeCompletionAt.Line = Line;
+ FrontendOpts.CodeCompletionAt.Column = Column;
+
+ // Set the language options appropriately.
+ LangOpts = CCInvocation.getLangOpts();
+
+ CompilerInstance Clang;
+ Clang.setInvocation(&CCInvocation);
+ OriginalSourceFile = Clang.getFrontendOpts().Inputs[0].second;
+
+ // Set up diagnostics, capturing any diagnostics produced.
+ Clang.setDiagnostics(&Diag);
+ CaptureDroppedDiagnostics Capture(true,
+ Clang.getDiagnostics(),
+ StoredDiagnostics);
+ Clang.setDiagnosticClient(Diag.getClient());
+
+ // Create the target instance.
+ Clang.setTarget(TargetInfo::CreateTargetInfo(Clang.getDiagnostics(),
+ Clang.getTargetOpts()));
+ if (!Clang.hasTarget()) {
+ Clang.takeDiagnosticClient();
+ Clang.takeInvocation();
+ }
+
+ // Inform the target of the language options.
+ //
+ // FIXME: We shouldn't need to do this, the target should be immutable once
+ // created. This complexity should be lifted elsewhere.
+ Clang.getTarget().setForcedLangOptions(Clang.getLangOpts());
+
+ assert(Clang.getFrontendOpts().Inputs.size() == 1 &&
+ "Invocation must have exactly one source file!");
+ assert(Clang.getFrontendOpts().Inputs[0].first != IK_AST &&
+ "FIXME: AST inputs not yet supported here!");
+ assert(Clang.getFrontendOpts().Inputs[0].first != IK_LLVM_IR &&
+ "IR inputs not support here!");
+
+
+ // Use the source and file managers that we were given.
+ Clang.setFileManager(&FileMgr);
+ Clang.setSourceManager(&SourceMgr);
+
+ // Remap files.
+ PreprocessorOpts.clearRemappedFiles();
+ for (unsigned I = 0; I != NumRemappedFiles; ++I)
+ PreprocessorOpts.addRemappedFile(RemappedFiles[I].first,
+ RemappedFiles[I].second);
+
+ // Use the code completion consumer we were given.
+ Clang.setCodeCompletionConsumer(&Consumer);
+
+ llvm::OwningPtr<SyntaxOnlyAction> Act;
+ Act.reset(new SyntaxOnlyAction);
+ if (Act->BeginSourceFile(Clang, Clang.getFrontendOpts().Inputs[0].second,
+ Clang.getFrontendOpts().Inputs[0].first)) {
+ Act->Execute();
+ Act->EndSourceFile();
+ }
+
+ // Steal back our resources.
+ Clang.takeFileManager();
+ Clang.takeSourceManager();
+ Clang.takeInvocation();
+ Clang.takeDiagnosticClient();
+ Clang.takeCodeCompletionConsumer();
+}
// Code Completion
+static bool EnableCodeCompletion(Preprocessor &PP,
+ const std::string &Filename,
+ unsigned Line,
+ unsigned Column) {
+ // Tell the source manager to chop off the given file at a specific
+ // line and column.
+ const FileEntry *Entry = PP.getFileManager().getFile(Filename);
+ if (!Entry) {
+ PP.getDiagnostics().Report(diag::err_fe_invalid_code_complete_file)
+ << Filename;
+ return true;
+ }
+
+ // Truncate the named file at the given line/column.
+ PP.SetCodeCompletionPoint(Entry, Line, Column);
+ return false;
+}
+
void CompilerInstance::createCodeCompletionConsumer() {
const ParsedSourceLocation &Loc = getFrontendOpts().CodeCompletionAt;
- CompletionConsumer.reset(
- createCodeCompletionConsumer(getPreprocessor(),
- Loc.FileName, Loc.Line, Loc.Column,
- getFrontendOpts().DebugCodeCompletionPrinter,
- getFrontendOpts().ShowMacrosInCodeCompletion,
+ if (!CompletionConsumer) {
+ CompletionConsumer.reset(
+ createCodeCompletionConsumer(getPreprocessor(),
+ Loc.FileName, Loc.Line, Loc.Column,
+ getFrontendOpts().DebugCodeCompletionPrinter,
+ getFrontendOpts().ShowMacrosInCodeCompletion,
getFrontendOpts().ShowCodePatternsInCodeCompletion,
- llvm::outs()));
- if (!CompletionConsumer)
+ llvm::outs()));
+ if (!CompletionConsumer)
+ return;
+ } else if (EnableCodeCompletion(getPreprocessor(), Loc.FileName,
+ Loc.Line, Loc.Column)) {
+ CompletionConsumer.reset();
return;
+ }
if (CompletionConsumer->isOutputBinary() &&
llvm::sys::Program::ChangeStdoutToBinary()) {
bool ShowMacros,
bool ShowCodePatterns,
llvm::raw_ostream &OS) {
- // Tell the source manager to chop off the given file at a specific
- // line and column.
- const FileEntry *Entry = PP.getFileManager().getFile(Filename);
- if (!Entry) {
- PP.getDiagnostics().Report(diag::err_fe_invalid_code_complete_file)
- << Filename;
+ if (EnableCodeCompletion(PP, Filename, Line, Column))
return 0;
- }
-
- // Truncate the named file at the given line/column.
- PP.SetCodeCompletionPoint(Entry, Line, Column);
// Set up the creation routine for code-completion.
if (UseDebugPrinter)
return 0;
}
-CodeCompletionString *CodeCompletionString::Clone() const {
- CodeCompletionString *Result = new CodeCompletionString;
+CodeCompletionString *
+CodeCompletionString::Clone(CodeCompletionString *Result) const {
+ if (!Result)
+ Result = new CodeCompletionString;
for (iterator C = begin(), CEnd = end(); C != CEnd; ++C)
Result->AddChunk(C->Clone());
return Result;
}
}
-void
-CIndexCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &SemaRef,
- Result *Results,
- unsigned NumResults) {
- // Print the results.
- for (unsigned I = 0; I != NumResults; ++I) {
- CXCursorKind Kind = CXCursor_NotImplemented;
-
- switch (Results[I].Kind) {
+namespace clang {
+ // FIXME: Used externally by CIndexCodeCompletion.cpp; this code
+ // will move there, eventually, when the CIndexCodeCompleteConsumer
+ // dies.
+ CXCursorKind
+ getCursorKindForCompletionResult(const CodeCompleteConsumer::Result &R) {
+ typedef CodeCompleteConsumer::Result Result;
+ switch (R.Kind) {
case Result::RK_Declaration:
- switch (Results[I].Declaration->getKind()) {
+ switch (R.Declaration->getKind()) {
case Decl::Record:
case Decl::CXXRecord:
case Decl::ClassTemplateSpecialization: {
- RecordDecl *Record = cast<RecordDecl>(Results[I].Declaration);
+ RecordDecl *Record = cast<RecordDecl>(R.Declaration);
if (Record->isStruct())
- Kind = CXCursor_StructDecl;
+ return CXCursor_StructDecl;
else if (Record->isUnion())
- Kind = CXCursor_UnionDecl;
+ return CXCursor_UnionDecl;
else
- Kind = CXCursor_ClassDecl;
- break;
+ return CXCursor_ClassDecl;
}
case Decl::ObjCMethod: {
- ObjCMethodDecl *Method = cast<ObjCMethodDecl>(Results[I].Declaration);
+ ObjCMethodDecl *Method = cast<ObjCMethodDecl>(R.Declaration);
if (Method->isInstanceMethod())
- Kind = CXCursor_ObjCInstanceMethodDecl;
+ return CXCursor_ObjCInstanceMethodDecl;
else
- Kind = CXCursor_ObjCClassMethodDecl;
- break;
+ return CXCursor_ObjCClassMethodDecl;
}
case Decl::Typedef:
- Kind = CXCursor_TypedefDecl;
- break;
+ return CXCursor_TypedefDecl;
case Decl::Enum:
- Kind = CXCursor_EnumDecl;
- break;
+ return CXCursor_EnumDecl;
case Decl::Field:
- Kind = CXCursor_FieldDecl;
- break;
+ return CXCursor_FieldDecl;
case Decl::EnumConstant:
- Kind = CXCursor_EnumConstantDecl;
- break;
+ return CXCursor_EnumConstantDecl;
case Decl::Function:
case Decl::CXXMethod:
case Decl::CXXConstructor:
case Decl::CXXDestructor:
case Decl::CXXConversion:
- Kind = CXCursor_FunctionDecl;
- break;
+ return CXCursor_FunctionDecl;
case Decl::Var:
- Kind = CXCursor_VarDecl;
- break;
+ return CXCursor_VarDecl;
case Decl::ParmVar:
- Kind = CXCursor_ParmDecl;
- break;
+ return CXCursor_ParmDecl;
case Decl::ObjCInterface:
- Kind = CXCursor_ObjCInterfaceDecl;
- break;
+ return CXCursor_ObjCInterfaceDecl;
case Decl::ObjCCategory:
- Kind = CXCursor_ObjCCategoryDecl;
- break;
+ return CXCursor_ObjCCategoryDecl;
case Decl::ObjCProtocol:
- Kind = CXCursor_ObjCProtocolDecl;
- break;
+ return CXCursor_ObjCProtocolDecl;
case Decl::ObjCProperty:
- Kind = CXCursor_ObjCPropertyDecl;
- break;
+ return CXCursor_ObjCPropertyDecl;
case Decl::ObjCIvar:
- Kind = CXCursor_ObjCIvarDecl;
- break;
+ return CXCursor_ObjCIvarDecl;
case Decl::ObjCImplementation:
- Kind = CXCursor_ObjCImplementationDecl;
- break;
+ return CXCursor_ObjCImplementationDecl;
case Decl::ObjCCategoryImpl:
- Kind = CXCursor_ObjCCategoryImplDecl;
- break;
+ return CXCursor_ObjCCategoryImplDecl;
default:
break;
break;
case Result::RK_Macro:
- Kind = CXCursor_MacroDefinition;
- break;
+ return CXCursor_MacroDefinition;
case Result::RK_Keyword:
case Result::RK_Pattern:
- Kind = CXCursor_NotImplemented;
- break;
+ return CXCursor_NotImplemented;
}
+ return CXCursor_NotImplemented;
+ }
+}
- WriteUnsigned(OS, Kind);
+void
+CIndexCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &SemaRef,
+ Result *Results,
+ unsigned NumResults) {
+ // Print the results.
+ for (unsigned I = 0; I != NumResults; ++I) {
+ WriteUnsigned(OS, getCursorKindForCompletionResult(Results[I]));
WriteUnsigned(OS, Results[I].Priority);
CodeCompletionString *CCS = Results[I].CreateCodeCompletionString(SemaRef);
assert(CCS && "No code-completion string?");
/// how to use this result, or NULL to indicate that the string or name of the
/// result is all that is needed.
CodeCompletionString *
-CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) {
+CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S,
+ CodeCompletionString *Result) {
typedef CodeCompletionString::Chunk Chunk;
if (Kind == RK_Pattern)
- return Pattern->Clone();
+ return Pattern->Clone(Result);
- CodeCompletionString *Result = new CodeCompletionString;
+ if (!Result)
+ Result = new CodeCompletionString;
if (Kind == RK_Keyword) {
Result->AddTypedTextChunk(Keyword);
const char *str = "Hello, \nWorld";
// RUN: c-index-test -code-completion-at=%s:7:9 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC1 %s
+// RUN: env CINDEXTEST_EDITING=1 c-index-test -code-completion-at=%s:7:9 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC1 %s
// CHECK-CC1: macro definition:{TypedText __VERSION__} (70)
// CHECK-CC1: FunctionDecl:{ResultType int}{TypedText f}{LeftParen (}{Placeholder int}{RightParen )} (12)
// CHECK-CC1-NOT: NotImplemented:{TypedText float} (40)
// CHECK-CC1: ParmDecl:{ResultType int}{TypedText j} (2)
// CHECK-CC1: NotImplemented:{TypedText sizeof}{LeftParen (}{Placeholder expression-or-type}{RightParen )} (30)
// RUN: c-index-test -code-completion-at=%s:7:14 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC3 %s
+// RUN: env CINDEXTEST_EDITING=1 c-index-test -code-completion-at=%s:7:14 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC3 %s
// CHECK-CC3: macro definition:{TypedText __VERSION__} (70)
// CHECK-CC3: FunctionDecl:{ResultType int}{TypedText f}{LeftParen (}{Placeholder int}{RightParen )} (50)
// CHECK-CC3-NOT: NotImplemented:{TypedText float} (40)
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;
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));
}
#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"
llvm::SmallVector<StoredDiagnostic, 8> Diagnostics;
/// \brief Diag object
- Diagnostic Diag;
+ llvm::IntrusiveRefCntPtr<Diagnostic> Diag;
/// \brief Language options used to adjust source locations.
LangOptions LangOpts;
};
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)
// destroyed.
Results->TemporaryFiles.swap(TemporaryFiles);
+#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;
+}
+
+} // 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();
_clang_CXXMethod_isStatic
_clang_annotateTokens
_clang_codeComplete
+_clang_codeCompleteAt
_clang_codeCompleteGetDiagnostic
_clang_codeCompleteGetNumDiagnostics
_clang_constructUSR_ObjCCategory
clang_CXXMethod_isStatic
clang_annotateTokens
clang_codeComplete
+clang_codeCompleteAt
clang_codeCompleteGetDiagnostic
clang_codeCompleteGetNumDiagnostics
clang_constructUSR_ObjCCategory