int main(int argc, char *argv[]) { CXIndex Index = clang_createIndex(0, 0); CXTranslationUnit TU; TU = clang_parseTranslationUnit(Index, 0, (const char**)argv, argc, 0, 0, CXTranslationUnit_None); CXCodeCompleteResults *results = clang_codeCompleteAt(TU, argv[1], 20, 7, NULL, 0, clang_defaultCodeCompleteOptions()); unsigned i; for(i =0; i<results->NumResults; i++) { printf("%s",results->Results[i].CompletionString); } // clang_disposeString(String); clang_disposeCodeCompleteResults(results); clang_disposeTranslationUnit(TU); clang_disposeIndex(Index); return 0; }
CXCodeCompleteResults* completion_codeCompleteAt(completion_Session *session, int line, int column) { struct CXUnsavedFile unsaved_files = __get_CXUnsavedFile(session); return clang_codeCompleteAt(session->cx_tu, session->src_filename, line, column, &unsaved_files, 1, session->CompleteAtOptions); }
ClangCodeCompleteResults CodeCompleter::complete(uint line, uint column, CXUnsavedFile *unsavedFiles, unsigned unsavedFileCount) { return clang_codeCompleteAt(translationUnit.cxTranslationUnitWithoutReparsing(), translationUnit.filePath().constData(), line, column, unsavedFiles, unsavedFileCount, defaultOptions()); }
ClangCodeCompleteResults CodeCompleter::completeHelper(uint line, uint column) { const Utf8String nativeFilePath = FilePath::toNativeSeparators(translationUnit.filePath()); UnsavedFilesShallowArguments unsaved = unsavedFiles.shallowArguments(); return clang_codeCompleteAt(translationUnit.cxTranslationUnit(), nativeFilePath.constData(), line, column, unsaved.data(), unsaved.count(), defaultOptions()); }
QVector<CodeCompletion> CodeCompleter::complete(uint line, uint column) { ClangCodeCompleteResults completeResults(clang_codeCompleteAt(translationUnit.cxTranslationUnit(), translationUnit.filePath().constData(), line, column, translationUnit.cxUnsavedFiles(), translationUnit.unsavedFilesCount(), CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeCodePatterns)); CodeCompletionsExtractor extractor(completeResults.data()); return extractor.extractAll(); }
ClangCodeCompleteResults CodeCompleter::complete(uint line, uint column, CXUnsavedFile *unsavedFiles, unsigned unsavedFileCount) { const auto options = CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeCodePatterns; return clang_codeCompleteAt(translationUnit.cxTranslationUnitWithoutReparsing(), translationUnit.filePath().constData(), line, column, unsavedFiles, unsavedFileCount, options); }
struct cc_result* cc_result_new(CXTranslationUnit tu, struct cc_trie* tp, struct cc_resultcache* cache, const char* filename, unsigned int line, unsigned int col, struct CXUnsavedFile* unsaved_files, unsigned int num_unsaved_files) { unsigned int i; CXCodeCompleteResults* result = clang_codeCompleteAt(tu, filename, line, col, unsaved_files, num_unsaved_files, _COMPLETE_OPTIONS); struct cc_result* rp = (struct cc_result*)malloc(sizeof(*rp)); cc_trie_clear(tp); rp->result = result; rp->ref_cache = cache; rp->ref_tp = tp; for(i=0; i<result->NumResults; i++) { _insert(rp, i); } return rp; }
OovStringVec Tokenizer::codeComplete(size_t offset) { CLangAutoLock lock(mCLangLock, __LINE__, this); OovStringVec strs; unsigned options = 0; // This gets more than we want. // unsigned options = clang_defaultCodeCompleteOptions(); unsigned int line; unsigned int column; getLineColumn(offset, line, column); CXCodeCompleteResults *results = clang_codeCompleteAt(mTransUnit, mSourceFilename.getStr(), line, column, nullptr, 0, options); if(results) { clang_sortCodeCompletionResults(&results->Results[0], results->NumResults); for(size_t ri=0; ri<results->NumResults /*&& ri < 50*/; ri++) { OovString str; CXCompletionString compStr = results->Results[ri].CompletionString; size_t numChunks = clang_getNumCompletionChunks(compStr); for(size_t ci=0; ci<numChunks && ci < 30; ci++) { CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(compStr, ci); // We will discard return values from functions, so the first // chunk returned will be the identifier or function name. Function // arguments will be returned after a space, so they can be // discarded easily. if(chunkKind == CXCompletionChunk_TypedText || str.length()) { std::string chunkStr = getDisposedString(clang_getCompletionChunkText(compStr, ci)); if(str.length() != 0) str += ' '; str += chunkStr; } } strs.push_back(str); } clang_disposeCodeCompleteResults(results); } return strs; }
std::vector< CompletionData > TranslationUnit::CandidatesForLocation( const std::string &filename, int line, int column, const std::vector< UnsavedFile > &unsaved_files ) { unique_lock< mutex > lock( clang_access_mutex_ ); if ( !clang_translation_unit_ ) { return std::vector< CompletionData >(); } std::vector< CXUnsavedFile > cxunsaved_files = ToCXUnsavedFiles( unsaved_files ); const CXUnsavedFile *unsaved = cxunsaved_files.empty() ? nullptr : &cxunsaved_files[ 0 ]; // codeCompleteAt reparses the TU if the underlying source file has changed on // disk since the last time the TU was updated and there are no unsaved files. // If there are unsaved files, then codeCompleteAt will parse the in-memory // file contents we are giving it. In short, it is NEVER a good idea to call // clang_reparseTranslationUnit right before a call to clang_codeCompleteAt. // This only makes clang reparse the whole file TWICE, which has a huge impact // on latency. At the time of writing, it seems that most users of libclang // in the open-source world don't realize this (I checked). Some don't even // call reparse*, but parse* which is even less efficient. CodeCompleteResultsWrap results( clang_codeCompleteAt( clang_translation_unit_, filename.c_str(), line, column, const_cast<CXUnsavedFile *>( unsaved ), cxunsaved_files.size(), CompletionOptions() ), clang_disposeCodeCompleteResults ); std::vector< CompletionData > candidates = ToCompletionDataVector( results.get() ); return candidates; }
void CompletionThread::process(Request *request) { // if (!request->unsaved.isEmpty()) { // int line = request->location.line(); // int pos = 0; // while (line > 1) { // int p = request->unsaved.indexOf('\n', pos); // if (p == -1) { // pos = -1; // break; // } // pos = p + 1; // --line; // } // if (pos != -1) { // int end = request->unsaved.indexOf('\n', pos); // if (end == -1) // end = request->unsaved.size(); // error("Completing at %s:%d:%d line: [%s]\n", // request->location.path().constData(), // request->location.line(), // request->location.column(), // request->unsaved.mid(pos, end - pos).constData()); // } // } StopWatch sw; int parseTime = 0; int reparseTime = 0; int completeTime = 0; int processTime = 0; SourceFile *&cache = mCacheMap[request->source.fileId]; if (cache && cache->source != request->source) { delete cache; cache = 0; } if (!cache) { cache = new SourceFile; mCacheList.append(cache); while (mCacheMap.size() > mCacheSize) { SourceFile *c = mCacheList.removeFirst(); mCacheMap.remove(c->source.fileId); delete c; } } else { mCacheList.moveToEnd(cache); } const bool sendDebug = testLog(LogLevel::Debug); if (cache->translationUnit && cache->source != request->source) { clang_disposeTranslationUnit(cache->translationUnit); cache->translationUnit = 0; cache->source = request->source; } else if (!cache->translationUnit) { cache->source = request->source; } const Path sourceFile = request->source.sourceFile(); CXUnsavedFile unsaved = { sourceFile.constData(), request->unsaved.constData(), static_cast<unsigned long>(request->unsaved.size()) }; size_t hash = 0; uint64_t lastModified = 0; if (request->unsaved.size()) { std::hash<String> h; hash = h(request->unsaved); } else { lastModified = sourceFile.lastModifiedMs(); } const auto &options = Server::instance()->options(); if (!cache->translationUnit) { cache->completionsMap.clear(); cache->completionsList.deleteAll(); sw.restart(); Flags<CXTranslationUnit_Flags> flags = static_cast<CXTranslationUnit_Flags>(clang_defaultEditingTranslationUnitOptions()); flags |= CXTranslationUnit_PrecompiledPreamble; flags |= CXTranslationUnit_CacheCompletionResults; flags |= CXTranslationUnit_SkipFunctionBodies; for (const auto &inc : options.includePaths) { request->source.includePaths << inc; } request->source.defines << options.defines; String clangLine; RTags::parseTranslationUnit(sourceFile, request->source.toCommandLine(Source::Default|Source::ExcludeDefaultArguments), cache->translationUnit, mIndex, &unsaved, request->unsaved.size() ? 1 : 0, flags, &clangLine); // error() << "PARSING" << clangLine; parseTime = sw.restart(); if (cache->translationUnit) { RTags::reparseTranslationUnit(cache->translationUnit, &unsaved, request->unsaved.size() ? 1 : 0); } reparseTime = sw.elapsed(); if (!cache->translationUnit) return; cache->unsavedHash = hash; cache->lastModified = lastModified; } else if (cache->unsavedHash != hash || cache->lastModified != lastModified) { cache->completionsMap.clear(); cache->completionsList.deleteAll(); cache->unsavedHash = hash; cache->lastModified = lastModified; } else if (!(request->flags & Refresh)) { const auto it = cache->completionsMap.find(request->location); if (it != cache->completionsMap.end()) { cache->completionsList.moveToEnd(it->second); error("Found completions (%d) in cache %s:%d:%d", it->second->candidates.size(), sourceFile.constData(), request->location.line(), request->location.column()); printCompletions(it->second->candidates, request); return; } } sw.restart(); const unsigned int completionFlags = (CXCodeComplete_IncludeMacros|CXCodeComplete_IncludeCodePatterns); CXCodeCompleteResults *results = clang_codeCompleteAt(cache->translationUnit, sourceFile.constData(), request->location.line(), request->location.column(), &unsaved, unsaved.Length ? 1 : 0, completionFlags); completeTime = sw.restart(); if (results) { std::vector<Completions::Candidate> nodes; nodes.reserve(results->NumResults); int nodeCount = 0; Map<Token, int> tokens; if (!request->unsaved.isEmpty()) { tokens = Token::tokenize(request->unsaved.constData(), request->unsaved.size()); // for (Map<Token, int>::const_iterator it = tokens.begin(); it != tokens.end(); ++it) { // error() << String(it->first.data, it->first.length) << it->second; // } } for (unsigned int i = 0; i < results->NumResults; ++i) { const CXCursorKind kind = results->Results[i].CursorKind; if (!(options.options & Server::CompletionsNoFilter) && kind == CXCursor_Destructor) continue; const CXCompletionString &string = results->Results[i].CompletionString; const CXAvailabilityKind availabilityKind = clang_getCompletionAvailability(string); if (!(options.options & Server::CompletionsNoFilter) && availabilityKind != CXAvailability_Available) continue; const int priority = clang_getCompletionPriority(string); if (size_t(nodeCount) == nodes.size()) nodes.emplace_back(); Completions::Candidate &node = nodes.back(); node.cursorKind = kind; node.priority = priority; node.signature.reserve(256); const int chunkCount = clang_getNumCompletionChunks(string); bool ok = true; for (int j=0; j<chunkCount; ++j) { const CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(string, j); if (chunkKind == CXCompletionChunk_TypedText) { node.completion = RTags::eatString(clang_getCompletionChunkText(string, j)); if (node.completion.size() > 8 && node.completion.startsWith("operator") && !isPartOfSymbol(node.completion.at(8))) { ok = false; break; } node.signature.append(node.completion); } else { node.signature.append(RTags::eatString(clang_getCompletionChunkText(string, j))); if (chunkKind == CXCompletionChunk_ResultType) node.signature.append(' '); } } if (ok) { int ws = node.completion.size() - 1; while (ws >= 0 && isspace(node.completion.at(ws))) --ws; if (ws >= 0) { node.completion.truncate(ws + 1); node.signature.replace("\n", ""); node.distance = tokens.value(Token(node.completion.constData(), node.completion.size()), -1); if (sendDebug) debug() << node.signature << node.priority << kind << node.distance << clang_getCompletionAvailability(string); ++nodeCount; continue; } } node.completion.clear(); node.signature.clear(); } if (nodeCount) { // Sort pointers instead of shuffling candidates around std::vector<Completions::Candidate*> nodesPtr; nodesPtr.reserve(nodeCount); for (auto& n : nodes) nodesPtr.push_back(&n); std::sort(nodesPtr.begin(), nodesPtr.end(), compareCompletionCandidates); Completions *&c = cache->completionsMap[request->location]; if (c) { cache->completionsList.moveToEnd(c); } else { enum { MaxCompletionCache = 10 }; // ### configurable? c = new Completions(request->location); cache->completionsList.append(c); while (cache->completionsMap.size() > MaxCompletionCache) { Completions *cc = cache->completionsList.takeFirst(); cache->completionsMap.remove(cc->location); delete cc; } } c->candidates.resize(nodeCount); for (int i=0; i<nodeCount; ++i) c->candidates[i] = std::move(*nodesPtr[i]); printCompletions(c->candidates, request); processTime = sw.elapsed(); warning("Processed %s, parse %d/%d, complete %d, process %d => %d completions (unsaved %d)", sourceFile.constData(), parseTime, reparseTime, completeTime, processTime, nodeCount, request->unsaved.size()); } else { printCompletions(List<Completions::Candidate>(), request); error() << "No completion results available" << request->location << results->NumResults; } clang_disposeCodeCompleteResults(results); } }
void CompletionJob::execute() { CXUnsavedFile unsavedFile = { mUnsaved.isEmpty() ? 0 : mPath.constData(), mUnsaved.isEmpty() ? 0 : mUnsaved.constData(), static_cast<unsigned long>(mUnsaved.size()) }; CXCodeCompleteResults *results = clang_codeCompleteAt(mUnit, mPath.constData(), mLine, mColumn, &unsavedFile, mUnsaved.isEmpty() ? 0 : 1, CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeCodePatterns); if (results) { CompletionNode *nodes = new CompletionNode[results->NumResults]; int nodeCount = 0; Map<Token, int> tokens; if (!mUnsaved.isEmpty()) { tokenize(mUnsaved.constData(), mUnsaved.size(), tokens); // for (Map<Token, int>::const_iterator it = tokens.begin(); it != tokens.end(); ++it) { // error() << ByteArray(it->first.data, it->first.length) << it->second; // } } for (unsigned i = 0; i < results->NumResults; ++i) { const CXCursorKind kind = results->Results[i].CursorKind; if (kind == CXCursor_Destructor) continue; const CXCompletionString &string = results->Results[i].CompletionString; const CXAvailabilityKind availabilityKind = clang_getCompletionAvailability(string); if (availabilityKind != CXAvailability_Available) continue; const int priority = clang_getCompletionPriority(string); if (priority >= 75) continue; CompletionNode &node = nodes[nodeCount]; node.priority = priority; node.signature.reserve(256); const int chunkCount = clang_getNumCompletionChunks(string); bool ok = true; for (int j=0; j<chunkCount; ++j) { const CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(string, j); if (chunkKind == CXCompletionChunk_TypedText) { node.completion = RTags::eatString(clang_getCompletionChunkText(string, j)); if (node.completion.size() > 8 && node.completion.startsWith("operator") && !isPartOfSymbol(node.completion.at(8))) { ok = false; break; } node.signature.append(node.completion); } else { node.signature.append(RTags::eatString(clang_getCompletionChunkText(string, j))); if (chunkKind == CXCompletionChunk_ResultType) node.signature.append(' '); } } if (ok) { int ws = node.completion.size() - 1; while (ws >= 0 && isspace(node.completion.at(ws))) --ws; if (ws >= 0) { node.completion.truncate(ws + 1); node.signature.replace("\n", ""); node.distance = tokens.value(Token(node.completion.constData(), node.completion.size()), -1); ++nodeCount; continue; } } node.completion.clear(); node.signature.clear(); } if (nodeCount) { qsort(nodes, nodeCount, sizeof(CompletionNode), compareCompletionNode); write<128>("`%s %s", nodes[0].completion.constData(), nodes[0].signature.constData()); for (int i=1; i<nodeCount; ++i) { write<128>("%s %s", nodes[i].completion.constData(), nodes[i].signature.constData()); } } delete[] nodes; //processDiagnostics(results); clang_disposeCodeCompleteResults(results); shared_ptr<Project> proj = project(); if (proj) proj->addToCache(mPath, mArgs, mIndex, mUnit); } mFinished(mPath); }
void ClangWorkerThread::ProcessRequest(ThreadRequest* request) { // Send start event PostEvent(wxEVT_CLANG_PCH_CACHE_STARTED, ""); ClangThreadRequest* task = dynamic_cast<ClangThreadRequest*>(request); wxASSERT_MSG(task, "ClangWorkerThread: NULL task"); { // A bit of optimization wxCriticalSectionLocker locker(m_criticalSection); if(task->GetContext() == CTX_CachePCH && m_cache.Contains(task->GetFileName())) { // Nothing to be done here PostEvent(wxEVT_CLANG_PCH_CACHE_ENDED, task->GetFileName()); return; } } CL_DEBUG(wxT("==========> [ ClangPchMakerThread ] ProcessRequest started: %s"), task->GetFileName().c_str()); CL_DEBUG(wxT("ClangWorkerThread:: processing request %d"), (int)task->GetContext()); ClangCacheEntry cacheEntry = findEntry(task->GetFileName()); CXTranslationUnit TU = cacheEntry.TU; CL_DEBUG(wxT("ClangWorkerThread:: found cached TU: %p"), (void*)TU); bool reparseRequired = true; if(!TU) { // First time creating the TU TU = DoCreateTU(task->GetIndex(), task, true); reparseRequired = false; cacheEntry.lastReparse = time(NULL); cacheEntry.TU = TU; cacheEntry.sourceFile = task->GetFileName(); } if(!TU) { CL_DEBUG(wxT("Failed to parse Translation UNIT...")); PostEvent(wxEVT_CLANG_TU_CREATE_ERROR, task->GetFileName()); return; } if(reparseRequired && task->GetContext() == ::CTX_ReparseTU) { DoSetStatusMsg(wxString::Format(wxT("clang: re-parsing file %s..."), task->GetFileName().c_str())); // We need to reparse the TU CL_DEBUG(wxT("Calling clang_reparseTranslationUnit... [CTX_ReparseTU]")); if(clang_reparseTranslationUnit(TU, 0, NULL, clang_defaultReparseOptions(TU)) == 0) { CL_DEBUG(wxT("Calling clang_reparseTranslationUnit... done [CTX_ReparseTU]")); cacheEntry.lastReparse = time(NULL); } else { CL_DEBUG(wxT("An error occured during reparsing of the TU for file %s. TU: %p"), task->GetFileName().c_str(), (void*)TU); // The only thing that left to be done here, is to dispose the TU clang_disposeTranslationUnit(TU); PostEvent(wxEVT_CLANG_TU_CREATE_ERROR, task->GetFileName()); return; } } // Construct a cache-returner class // which makes sure that the TU is cached // when we leave the current scope CacheReturner cr(this, cacheEntry); // Prepare the 'End' event wxCommandEvent eEnd(wxEVT_CLANG_PCH_CACHE_ENDED); ClangThreadReply* reply = new ClangThreadReply; reply->context = task->GetContext(); reply->filterWord = task->GetFilterWord(); reply->filename = task->GetFileName().c_str(); reply->results = NULL; wxFileName realFileName(reply->filename); if(realFileName.GetFullName().StartsWith(CODELITE_CLANG_FILE_PREFIX)) { realFileName.SetFullName(realFileName.GetFullName().Mid(strlen(CODELITE_CLANG_FILE_PREFIX))); } reply->filename = realFileName.GetFullPath(); if(task->GetContext() == CTX_CodeCompletion || task->GetContext() == CTX_WordCompletion || task->GetContext() == CTX_Calltip) { CL_DEBUG(wxT("Calling clang_codeCompleteAt...")); ClangThreadRequest::List_t usList = task->GetModifiedBuffers(); usList.push_back(std::make_pair(task->GetFileName(), task->GetDirtyBuffer())); ClangUnsavedFiles usf(usList); CL_DEBUG(wxT("Location: %s:%u:%u"), task->GetFileName().c_str(), task->GetLine(), task->GetColumn()); reply->results = clang_codeCompleteAt(TU, cstr(task->GetFileName()), task->GetLine(), task->GetColumn(), usf.GetUnsavedFiles(), usf.GetCount(), clang_defaultCodeCompleteOptions() #if HAS_LIBCLANG_BRIEFCOMMENTS | CXCodeComplete_IncludeBriefComments #endif ); cacheEntry.lastReparse = time(NULL); CL_DEBUG(wxT("Calling clang_codeCompleteAt... done")); wxString displayTip; bool hasErrors(false); if(reply->results) { unsigned maxErrorToDisplay = 10; std::set<wxString> errorMessages; unsigned errorCount = clang_codeCompleteGetNumDiagnostics(reply->results); // Collect all errors / fatal errors and report them back to user for(unsigned i = 0; i < errorCount; i++) { CXDiagnostic diag = clang_codeCompleteGetDiagnostic(reply->results, i); CXDiagnosticSeverity severity = clang_getDiagnosticSeverity(diag); if(!hasErrors) { hasErrors = (severity == CXDiagnostic_Error || severity == CXDiagnostic_Fatal); } if(severity == CXDiagnostic_Error || severity == CXDiagnostic_Fatal || severity == CXDiagnostic_Note) { CXString diagStr = clang_getDiagnosticSpelling(diag); wxString wxDiagString = wxString(clang_getCString(diagStr), wxConvUTF8); // Collect up to 10 error messages // and dont collect the same error twice if(errorMessages.find(wxDiagString) == errorMessages.end() && errorMessages.size() <= maxErrorToDisplay) { errorMessages.insert(wxDiagString); displayTip << wxDiagString.c_str() << wxT("\n"); } clang_disposeString(diagStr); } clang_disposeDiagnostic(diag); } CL_DEBUG(wxT("Found %u matches"), reply->results->NumResults); ClangUtils::printCompletionDiagnostics(reply->results); } if(!displayTip.IsEmpty() && hasErrors) { // Send back the error messages reply->errorMessage << "clang: " << displayTip; reply->errorMessage.RemoveLast(); // Free the results clang_disposeCodeCompleteResults(reply->results); reply->results = NULL; } // Send the event eEnd.SetClientData(reply); EventNotifier::Get()->AddPendingEvent(eEnd); } else if(task->GetContext() == CTX_GotoDecl || task->GetContext() == CTX_GotoImpl) { // Check to see if the file was modified since it was last reparsed // If it does, we need to re-parse it again wxFileName fnSource(cacheEntry.sourceFile); time_t fileModificationTime = fnSource.GetModificationTime().GetTicks(); time_t lastReparseTime = cacheEntry.lastReparse; if(fileModificationTime > lastReparseTime) { // The file needs to be re-parsed DoSetStatusMsg(wxString::Format(wxT("clang: re-parsing file %s...\n"), cacheEntry.sourceFile)); // Try reparsing the TU ClangThreadRequest::List_t usList = task->GetModifiedBuffers(); usList.push_back(std::make_pair(task->GetFileName(), task->GetDirtyBuffer())); ClangUnsavedFiles usf(usList); if(clang_reparseTranslationUnit( TU, usf.GetCount(), usf.GetUnsavedFiles(), clang_defaultReparseOptions(TU)) != 0) { // Failed to reparse cr.SetCancelled(true); // cancel the re-caching of the TU DoSetStatusMsg( wxString::Format("clang: clang_reparseTranslationUnit '%s' failed\n", cacheEntry.sourceFile)); clang_disposeTranslationUnit(TU); wxDELETE(reply); PostEvent(wxEVT_CLANG_TU_CREATE_ERROR, task->GetFileName()); return; } DoSetStatusMsg( wxString::Format("clang: clang_reparseTranslationUnit '%s' - done\n", cacheEntry.sourceFile)); // Update the 'lastReparse' field cacheEntry.lastReparse = time(NULL); } bool success = DoGotoDefinition(TU, task, reply); if(success) { eEnd.SetClientData(reply); EventNotifier::Get()->AddPendingEvent(eEnd); } else { DoSetStatusMsg(wxT("clang: no matches were found")); CL_DEBUG(wxT("Clang Goto Decl/Impl: could not find a cursor matching for position %s:%d:%d"), task->GetFileName().c_str(), (int)task->GetLine(), (int)task->GetColumn()); // Failed, delete the 'reply' allocatd earlier wxDELETE(reply); PostEvent(wxEVT_CLANG_TU_CREATE_ERROR, task->GetFileName()); } } else { wxDELETE(reply); PostEvent(wxEVT_CLANG_PCH_CACHE_ENDED, task->GetFileName()); } }
code_complete_results translation_unit::codeCompleteAt(const std::string& filename, unsigned line, unsigned column, const std::vector<unsaved_file>& unsaved) { auto unsaved_files = convert_unsaved_files(unsaved); auto options = clang_defaultCodeCompleteOptions(); return { clang_codeCompleteAt(tu, filename.c_str(), line, column, unsaved_files.data(), unsaved_files.size(), options) }; }
void Irony::complete(const std::string &file, unsigned line, unsigned col, const std::vector<std::string> &flags, const std::vector<CXUnsavedFile> &unsavedFiles) { CXTranslationUnit tu = tuManager_.getOrCreateTU(file, flags, unsavedFiles); if (tu == nullptr) { std::cout << "nil\n"; return; } if (CXCodeCompleteResults *completions = clang_codeCompleteAt(tu, file.c_str(), line, col, const_cast<CXUnsavedFile *>(unsavedFiles.data()), unsavedFiles.size(), (clang_defaultCodeCompleteOptions() & ~CXCodeComplete_IncludeCodePatterns) #if HAS_BRIEF_COMMENTS_IN_COMPLETION | CXCodeComplete_IncludeBriefComments #endif )) { if (debug_) { unsigned numDiags = clang_codeCompleteGetNumDiagnostics(completions); std::clog << "debug: complete: " << numDiags << " diagnostic(s)\n"; for (unsigned i = 0; i < numDiags; ++i) { CXDiagnostic diagnostic = clang_codeCompleteGetDiagnostic(completions, i); CXString s = clang_formatDiagnostic( diagnostic, clang_defaultDiagnosticDisplayOptions()); std::clog << clang_getCString(s) << std::endl; clang_disposeString(s); clang_disposeDiagnostic(diagnostic); } } clang_sortCodeCompletionResults(completions->Results, completions->NumResults); std::cout << "(\n"; // re-use the same buffers to avoid unnecessary allocations std::string typedtext, brief, resultType, prototype, postCompCar; std::vector<unsigned> postCompCdr; for (unsigned i = 0; i < completions->NumResults; ++i) { CXCompletionResult candidate = completions->Results[i]; CXAvailabilityKind availability = clang_getCompletionAvailability(candidate.CompletionString); unsigned priority = clang_getCompletionPriority(candidate.CompletionString); unsigned annotationStart = 0; bool typedTextSet = false; if (availability == CXAvailability_NotAccessible || availability == CXAvailability_NotAvailable) { continue; } typedtext.clear(); brief.clear(); resultType.clear(); prototype.clear(); postCompCar.clear(); postCompCdr.clear(); for (CompletionChunk chunk(candidate.CompletionString); chunk.hasNext(); chunk.next()) { char ch = 0; auto chunkKind = chunk.kind(); switch (chunkKind) { case CXCompletionChunk_ResultType: resultType = chunk.text(); break; case CXCompletionChunk_TypedText: case CXCompletionChunk_Text: case CXCompletionChunk_Placeholder: case CXCompletionChunk_Informative: case CXCompletionChunk_CurrentParameter: prototype += chunk.text(); break; case CXCompletionChunk_LeftParen: ch = '('; break; case CXCompletionChunk_RightParen: ch = ')'; break; case CXCompletionChunk_LeftBracket: ch = '['; break; case CXCompletionChunk_RightBracket: ch = ']'; break; case CXCompletionChunk_LeftBrace: ch = '{'; break; case CXCompletionChunk_RightBrace: ch = '}'; break; case CXCompletionChunk_LeftAngle: ch = '<'; break; case CXCompletionChunk_RightAngle: ch = '>'; break; case CXCompletionChunk_Comma: ch = ','; break; case CXCompletionChunk_Colon: ch = ':'; break; case CXCompletionChunk_SemiColon: ch = ';'; break; case CXCompletionChunk_Equal: ch = '='; break; case CXCompletionChunk_HorizontalSpace: ch = ' '; break; case CXCompletionChunk_VerticalSpace: ch = '\n'; break; case CXCompletionChunk_Optional: // ignored for now break; } if (ch != 0) { prototype += ch; // commas look better followed by a space if (ch == ',') { prototype += ' '; } } if (typedTextSet) { if (ch != 0) { postCompCar += ch; if (ch == ',') { postCompCar += ' '; } } else if (chunkKind == CXCompletionChunk_Text || chunkKind == CXCompletionChunk_TypedText) { postCompCar += chunk.text(); } else if (chunkKind == CXCompletionChunk_Placeholder || chunkKind == CXCompletionChunk_CurrentParameter) { postCompCdr.push_back(postCompCar.size()); postCompCar += chunk.text(); postCompCdr.push_back(postCompCar.size()); } } // Consider only the first typed text. The CXCompletionChunk_TypedText // doc suggests that exactly one typed text will be given but at least // in Objective-C it seems that more than one can appear, see: // https://github.com/Sarcasm/irony-mode/pull/78#issuecomment-37115538 if (chunkKind == CXCompletionChunk_TypedText && !typedTextSet) { typedtext = chunk.text(); // annotation is what comes after the typedtext annotationStart = prototype.size(); typedTextSet = true; } } #if HAS_BRIEF_COMMENTS_IN_COMPLETION brief = cxStringToStd( clang_getCompletionBriefComment(candidate.CompletionString)); #endif // see irony-completion.el#irony-completion-candidates std::cout << '(' << support::quoted(typedtext) // << ' ' << priority // << ' ' << support::quoted(resultType) // << ' ' << support::quoted(brief) // << ' ' << support::quoted(prototype) // << ' ' << annotationStart // << " (" << support::quoted(postCompCar); for (unsigned index : postCompCdr) std::cout << ' ' << index; std::cout << ")" << ")\n"; } clang_disposeCodeCompleteResults(completions); std::cout << ")\n"; } }
completion_list translation_unit::complete_at(uint32_t row, uint32_t col) { completion_list ret; CXCodeCompleteResults *res; if (mCxUnsaved) { res = clang_codeCompleteAt(mUnit, mName.c_str(), row, col, mCxUnsaved, 1, 0); } else res = clang_codeCompleteAt(mUnit, mName.c_str(), row, col, nullptr, 0, 0); for (uint32_t i = 0; i < res->NumResults; ++i) { // skip all private members if (clang_getCompletionAvailability(res->Results[i].CompletionString) == CXAvailability_NotAccessible) continue; // number of completion chunks for the current result completion_result r; uint32_t nChunks = clang_getNumCompletionChunks(res->Results[i].CompletionString); // function to handle a single chunk auto handle_chunk = [&](CXCompletionChunkKind k, uint32_t num) { CXString txt = clang_getCompletionChunkText(res->Results[i].CompletionString, num); switch (k) { case CXCompletionChunk_ResultType: r.return_type = cx2std(txt); break; case CXCompletionChunk_TypedText: r.name = cx2std(txt); break; case CXCompletionChunk_Placeholder: r.args.push_back(cx2std(txt)); break; case CXCompletionChunk_Optional: case CXCompletionChunk_LeftParen: case CXCompletionChunk_RightParen: case CXCompletionChunk_RightBracket: case CXCompletionChunk_LeftBracket: case CXCompletionChunk_LeftBrace: case CXCompletionChunk_RightBrace: case CXCompletionChunk_RightAngle: case CXCompletionChunk_LeftAngle: case CXCompletionChunk_Comma: case CXCompletionChunk_Colon: case CXCompletionChunk_SemiColon: case CXCompletionChunk_Equal: case CXCompletionChunk_Informative: case CXCompletionChunk_HorizontalSpace: break; default: break; } }; for (uint32_t k = 0; k < nChunks; ++k) { handle_chunk(clang_getCompletionChunkKind(res->Results[i].CompletionString, k), k); } // fill additional info and append to result set r.brief = cx2std(clang_getCompletionBriefComment(res->Results[i].CompletionString)); r.priority = clang_getCompletionPriority(res->Results[i].CompletionString); r.type = cursor2completion(res->Results[i].CursorKind); // @todo: once clang forwards the CXCursor of a completion result, we should get // the full documentation for each entry ret.push_back(r); } clang_disposeCodeCompleteResults(res); return ret; }
void CompletionJob::execute() { StopWatch timer; CXUnsavedFile unsavedFile = { mUnsaved.isEmpty() ? 0 : mPath.constData(), mUnsaved.isEmpty() ? 0 : mUnsaved.constData(), static_cast<unsigned long>(mUnsaved.size()) }; if (!mUnit) { String clangLine; RTags::parseTranslationUnit(mPath, mArgs, mUnit, Server::instance()->clangIndex(), clangLine, 0, 0, &unsavedFile, 1); mParseCount = 1; if (!mUnit) { error() << "Failed to parse" << mPath << "Can't complete"; return; } } // error() << "Completing" << mPath << mParseCount; assert(mParseCount >= 1 && mParseCount <= 2); if (mParseCount == 1) { RTags::reparseTranslationUnit(mUnit, &unsavedFile, 1); if (!mUnit) { mFinished(mPath, id()); return; } else { ++mParseCount; } } CXCodeCompleteResults *results = clang_codeCompleteAt(mUnit, mPath.constData(), mLine, mColumn, &unsavedFile, mUnsaved.isEmpty() ? 0 : 1, CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeCodePatterns); if (results) { CompletionNode *nodes = new CompletionNode[results->NumResults]; int nodeCount = 0; Map<Token, int> tokens; if (!mUnsaved.isEmpty()) { tokenize(mUnsaved.constData(), mUnsaved.size(), tokens); // for (Map<Token, int>::const_iterator it = tokens.begin(); it != tokens.end(); ++it) { // error() << String(it->first.data, it->first.length) << it->second; // } } for (unsigned i = 0; i < results->NumResults; ++i) { const CXCursorKind kind = results->Results[i].CursorKind; if (kind == CXCursor_Destructor) continue; const CXCompletionString &string = results->Results[i].CompletionString; const CXAvailabilityKind availabilityKind = clang_getCompletionAvailability(string); if (availabilityKind != CXAvailability_Available) continue; const int priority = clang_getCompletionPriority(string); if (priority >= 75) continue; CompletionNode &node = nodes[nodeCount]; node.priority = priority; node.signature.reserve(256); const int chunkCount = clang_getNumCompletionChunks(string); bool ok = true; for (int j=0; j<chunkCount; ++j) { const CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(string, j); if (chunkKind == CXCompletionChunk_TypedText) { node.completion = RTags::eatString(clang_getCompletionChunkText(string, j)); if (node.completion.size() > 8 && node.completion.startsWith("operator") && !isPartOfSymbol(node.completion.at(8))) { ok = false; break; } node.signature.append(node.completion); } else { node.signature.append(RTags::eatString(clang_getCompletionChunkText(string, j))); if (chunkKind == CXCompletionChunk_ResultType) node.signature.append(' '); } } if (ok) { int ws = node.completion.size() - 1; while (ws >= 0 && isspace(node.completion.at(ws))) --ws; if (ws >= 0) { node.completion.truncate(ws + 1); node.signature.replace("\n", ""); node.distance = tokens.value(Token(node.completion.constData(), node.completion.size()), -1); ++nodeCount; continue; } } node.completion.clear(); node.signature.clear(); } if (nodeCount) { if (nodeCount > SendThreshold) { write("`"); } else { qsort(nodes, nodeCount, sizeof(CompletionNode), compareCompletionNode); if (mType == Stream) { write<128>("`%s %s", nodes[0].completion.constData(), nodes[0].signature.constData()); } else { write<128>("%s %s", nodes[0].completion.constData(), nodes[0].signature.constData()); } for (int i=1; i<nodeCount; ++i) { write<128>("%s %s", nodes[i].completion.constData(), nodes[i].signature.constData()); } } } warning() << "Wrote" << ((nodeCount > SendThreshold) ? -1 : nodeCount) << "completions for" << String::format<128>("%s:%d:%d", mPath.constData(), mLine, mColumn) << "in" << timer.elapsed() << "ms" << mArgs; // const unsigned diagnosticCount = clang_getNumDiagnostics(mUnit); // for (unsigned i=0; i<diagnosticCount; ++i) { // CXDiagnostic diagnostic = clang_getDiagnostic(mUnit, i); // const CXDiagnosticSeverity severity = clang_getDiagnosticSeverity(diagnostic); // const String msg = RTags::eatString(clang_getDiagnosticSpelling(diagnostic)); // CXFile file; // unsigned line, col; // clang_getSpellingLocation(clang_getDiagnosticLocation(diagnostic), &file, &line, &col, 0); // error() << i << diagnosticCount << severity << msg // << String::format<128>("%s:%d:%d", RTags::eatString(clang_getFileName(file)).constData(), // line, col); // clang_disposeDiagnostic(diagnostic); // } delete[] nodes; //processDiagnostics(results); clang_disposeCodeCompleteResults(results); std::shared_ptr<Project> proj = project(); if (proj) { // error() << "Adding to cache" << mParseCount << mPath; proj->addToCache(mPath, mArgs, mUnit, mParseCount); } } mFinished(mPath, id()); }
QList<ClangCodeCompletionItem> TranslationUnit::completeAt( const int line , const int column , const unsigned completion_flags , const clang::unsaved_files_list& unsaved_files , const PluginConfiguration::sanitize_rules_list_type& sanitize_rules ) { auto files = unsaved_files.get(); #ifndef NDEBUG for (auto& item : files) assert( "Sanity check" && item.Filename && std::strlen(item.Filename) && item.Contents && item.Length ); #endif clang::DCXCodeCompleteResults res = { clang_codeCompleteAt( m_unit , m_filename.constData() , unsigned(line) , unsigned(column) , files.data() , files.size() , completion_flags ) }; if (!res) { throw Exception::CompletionFailure( i18nc("@item:intext", "Unable to perform code completion").toAscii().constData() ); } #if 0 clang_sortCodeCompletionResults(res->Results, res->NumResults); #endif // Collect some diagnostic SPAM for (auto i = 0u; i < clang_codeCompleteGetNumDiagnostics(res); ++i) { clang::DCXDiagnostic diag = {clang_codeCompleteGetDiagnostic(res, i)}; appendDiagnostic(diag); } QList<ClangCodeCompletionItem> completions; completions.reserve(res->NumResults); // Peallocate enough space for completion results // Lets look what we've got... for (auto i = 0u; i < res->NumResults; ++i) { const auto str = res->Results[i].CompletionString; const auto priority = clang_getCompletionPriority(str); const auto cursor_kind = res->Results[i].CursorKind; debugShowCompletionResult(i, priority, str, cursor_kind); // Skip unusable completions // 0) check availability const auto availability = clang_getCompletionAvailability(str); if (availability != CXAvailability_Available && availability != CXAvailability_Deprecated) { kDebug(DEBUG_AREA) << "!! Skip result" << i << "as not available"; continue; } // 1) check usefulness /// \todo Make it configurable if (cursor_kind == CXCursor_NotImplemented) continue; // Collect all completion chunks and from a format string QString text_before; QString typed_text; QString text_after; QStringList placeholders; int optional_placeholers_start_position = -1; // A lambda to append given text to different parts // of future completion string, depending on already processed text auto appender = [&](const QString& text) { if (typed_text.isEmpty()) text_before += text; else text_after += text; }; auto skip_this_item = false; for ( auto j = 0u , chunks = clang_getNumCompletionChunks(str) ; j < chunks && !skip_this_item ; ++j ) { auto kind = clang_getCompletionChunkKind(str, j); auto text = toString(clang::DCXString{clang_getCompletionChunkText(str, j)}); switch (kind) { // Text that a user would be expected to type to get this code-completion result case CXCompletionChunk_TypedText: // Text that should be inserted as part of a code-completion result case CXCompletionChunk_Text: { auto p = sanitize(text, sanitize_rules);// Pipe given piece of text through sanitizer if (p.first) typed_text += p.second; else // Go for next completion item skip_this_item = true; break; } // Placeholder text that should be replaced by the user case CXCompletionChunk_Placeholder: { auto p = sanitize(text, sanitize_rules);// Pipe given piece of text through sanitizer if (p.first) { appender( QLatin1String{"%"} + QString::number(placeholders.size() + 1) + QLatin1String{"%"} ); placeholders.push_back(p.second); } else // Go for next completion item skip_this_item = true; break; } // A code-completion string that describes "optional" text that // could be a part of the template (but is not required) case CXCompletionChunk_Optional: { auto ostr = clang_getCompletionChunkCompletionString(str, j); for ( auto oci = 0u , ocn = clang_getNumCompletionChunks(ostr) ; oci < ocn ; ++oci ) { auto otext = toString(clang::DCXString{clang_getCompletionChunkText(ostr, oci)}); // Pipe given piece of text through sanitizer auto p = sanitize(otext, sanitize_rules); if (p.first) { auto okind = clang::kind_of(ostr, oci); if (okind == CXCompletionChunk_Placeholder) { appender( QLatin1String{"%"} + QString::number(placeholders.size() + 1) + QLatin1String{"%"} ); placeholders.push_back(p.second); optional_placeholers_start_position = placeholders.size(); } else appender(p.second); } else { skip_this_item = true; break; } } break; } case CXCompletionChunk_ResultType: case CXCompletionChunk_LeftParen: case CXCompletionChunk_RightParen: case CXCompletionChunk_LeftBracket: case CXCompletionChunk_RightBracket: case CXCompletionChunk_LeftBrace: case CXCompletionChunk_RightBrace: case CXCompletionChunk_LeftAngle: case CXCompletionChunk_RightAngle: case CXCompletionChunk_Comma: case CXCompletionChunk_Colon: case CXCompletionChunk_SemiColon: case CXCompletionChunk_Equal: case CXCompletionChunk_CurrentParameter: case CXCompletionChunk_HorizontalSpace: /// \todo Kate can't handle \c '\n' well in completions list case CXCompletionChunk_VerticalSpace: { auto p = sanitize(text, sanitize_rules);// Pipe given piece of text through sanitizer if (p.first) appender(p.second); else // Go for next completion item skip_this_item = true; break; } // Informative text that should be displayed but never inserted // as part of the template case CXCompletionChunk_Informative: // Informative text before CXCompletionChunk_TypedText usually // just a method scope (i.e. long name of an owner class) // and it's useless for completer cuz it can group items // by parent already... if (!typed_text.isEmpty()) { // Pipe given piece of text through sanitizer auto p = sanitize(text, sanitize_rules); if (p.first) appender(p.second); else // Go for next completion item skip_this_item = true; } break; default: break; } } // Does it pass the completion items sanitizer? if (skip_this_item) continue; // No! Skip it! assert("Priority expected to be less than 100" && priority < 101u); const auto comment = toString(clang::DCXString{clang_getCompletionBriefComment(str)}); // completions.push_back({ makeParentText(str, cursor_kind) , text_before , typed_text , text_after , placeholders , optional_placeholers_start_position , priority , cursor_kind , comment , availability == CXAvailability_Deprecated }); } return completions; }
static void process_ac(int sock) { tpl_node *tn; struct msg_ac msg; wordexp_t flags; tn = msg_ac_node(&msg); tpl_load(tn, TPL_FD, sock); tpl_unpack(tn, 0); tpl_free(tn); struct CXUnsavedFile unsaved = { msg.filename, msg.buffer.addr, msg.buffer.sz }; change_dir(msg.filename); try_load_dotccode(&flags); str_t *partial = extract_partial(&msg); if (partial) msg.col -= partial->len; if (needs_reparsing(&flags, msg.filename)) { if (clang_tu) clang_disposeTranslationUnit(clang_tu); clang_tu = clang_parseTranslationUnit(clang_index, msg.filename, (char const * const *)flags.we_wordv, flags.we_wordc, &unsaved, 1, clang_defaultEditingTranslationUnitOptions()); if (last_filename) free(last_filename); if (last_wordexp.we_wordv) wordfree(&last_wordexp); last_filename = strdup(msg.filename); last_wordexp = flags; } // diag /* for (int i = 0, n = clang_getNumDiagnostics(clang_tu); i != n; ++i) { CXDiagnostic diag = clang_getDiagnostic(clang_tu, i); CXString string = clang_formatDiagnostic(diag, clang_defaultDiagnosticDisplayOptions()); fprintf(stderr, "%s\n", clang_getCString(string)); clang_disposeString(string); clang_disposeDiagnostic(diag); } */ CXCodeCompleteResults *results; results = clang_codeCompleteAt(clang_tu, msg.filename, msg.line, msg.col, &unsaved, 1, CXCodeComplete_IncludeMacros); free_msg_ac(&msg); // diag /* for (int i = 0, n = clang_codeCompleteGetNumDiagnostics(results); i != n; ++i) { CXDiagnostic diag = clang_codeCompleteGetDiagnostic(results, i); CXString string = clang_formatDiagnostic(diag, clang_defaultDiagnosticDisplayOptions()); fprintf(stderr, "%s\n", clang_getCString(string)); clang_disposeString(string); clang_disposeDiagnostic(diag); } */ struct msg_ac_response msg_r = { (partial) ? partial->len : 0, 0, 0 }; if (results) { struct make_ac_ctx ctx; str_t *fmt; init_make_ac_ctx(&ctx); msg_r.proposals_n = filter_out_cc_results(results->Results, results->NumResults, partial, &fmt); sort_cc_results(results->Results, msg_r.proposals_n); if (msg_r.proposals_n > MAX_AC_RESULTS) msg_r.proposals_n = MAX_AC_RESULTS; msg_r.proposals = malloc(sizeof(struct ac_proposal) * msg_r.proposals_n); int cur = 0; for (int i = 0; i < msg_r.proposals_n; ++i) { int added; added = make_ac_proposal(&ctx, &msg_r.proposals[cur], &results->Results[i], fmt); if (added) cur++; } msg_r.proposals_n = cur; free_make_ac_ctx(&ctx); str_free(fmt); } if (partial) str_free(partial); clang_disposeCodeCompleteResults(results); msg_ac_response_send(&msg_r, sock); free_msg_ac_response(&msg_r); }
//setCurrentCharFormat(m_formatKeyword); currentCursor.setCharFormat(m_formatMacro); /* Highlight *hi = new Highlight(); hi->setCXFile(file); hi->setCXSourceRange(range); hi->setCXTranslationUnit(cx_tu); hi->setDocument(this->document());*/ // ObjcHighlighter *h = new ObjcHighlighter(this->document()); //qDebug()<<tokenCount; int currentLine = this->textCursor().blockNumber()+1; int currentColumn = this->textCursor().columnNumber(); qDebug() << currentLine <<currentColumn<<endl; CXCodeCompleteResults *result = clang_codeCompleteAt( cx_tu, "/tmp/a.m", currentLine, currentColumn, &unsaved_file, 1, CXCodeComplete_IncludeMacros); clang_sortCodeCompletionResults(result->Results, result->NumResults); FILE * fp = fopen("/tmp/log.txt","w"); completion_printCodeCompletionResults(result, fp); fclose(fp); int maxwidth = 0; for ( int i = 0; i < result->NumResults; i++) { CXCompletionString cx_str= result->Results[i].CompletionString; CXString ac_string; for(int i = 0; i < clang_getNumCompletionChunks(cx_str); i++) { if(clang_getCompletionChunkKind(cx_str,i) == CXCompletionChunk_TypedText) { ac_string= clang_getCompletionChunkText(cx_str, i);