ClangUnsavedFiles::ClangUnsavedFiles(const ClangThreadRequest::List_t& files) : m_files(files) { m_unsavedFiles = new CXUnsavedFile[files.size()]; ClangThreadRequest::List_t::const_iterator iter = files.begin(); for(size_t i=0; iter != files.end(); ++iter, i++ ) { m_unsavedFiles[i].Contents = strdup( iter->second.mb_str(wxConvUTF8).data() ); m_unsavedFiles[i].Filename = strdup( iter->first.mb_str(wxConvUTF8).data() ); m_unsavedFiles[i].Length = strlen(m_unsavedFiles[i].Contents); } }
ClangThreadRequest::List_t ClangDriver::DoCreateListOfModifiedBuffers(IEditor* excludeEditor) { // Collect all modified buffers and pass them to clang as well ClangThreadRequest::List_t modifiedBuffers; std::vector<LEditor*> editors; clMainFrame::Get()->GetMainBook()->GetAllEditors(editors, MainBook::kGetAll_IncludeDetached); for(size_t i = 0; i < editors.size(); i++) { if(editors.at(i) == excludeEditor || !editors.at(i)->IsModified()) continue; modifiedBuffers.push_back(std::make_pair(editors.at(i)->GetFileName().GetFullPath(), editors.at(i)->GetText())); } return modifiedBuffers; }
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()); } }