void PHPEditorContextMenu::OnPopupClicked(wxCommandEvent& event) { IEditor* editor = m_manager->GetActiveEditor(); if(editor && IsPHPFile(editor)) { switch(event.GetId()) { case wxID_OPEN_PHP_FILE: DoOpenPHPFile(); break; case wxID_GOTO_DEFINITION: DoGotoDefinition(); break; case wxID_FIND_REFERENCES: // DoFindReferences(); break; default: event.Skip(); break; } } else { event.Skip(); } }
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()); } }
void ClangDriver::OnPrepareTUEnded(wxCommandEvent& e) { // Our thread is done m_isBusy = false; // Sanity ClangThreadReply* reply = (ClangThreadReply*)e.GetClientData(); if(!reply) { return; } // Make sure we delete the reply at the end... std::auto_ptr<ClangThreadReply> ap(reply); // Delete the fake file... DoDeleteTempFile(reply->filename); // Just a notification without real info? if(reply->context == CTX_None) { return; } if(reply->context == ::CTX_CachePCH || reply->context == ::CTX_ReparseTU) { return; // Nothing more to be done } if(reply->context == CTX_GotoDecl || reply->context == CTX_GotoImpl) { // Unlike other context's the 'filename' specified here // does not belong to an editor (it could, but it is not necessarily true) DoGotoDefinition(reply); return; } // Adjust the activeEditor to fit the filename IEditor* editor = clMainFrame::Get()->GetMainBook()->FindEditor(reply->filename); if(!editor) { CL_DEBUG(wxT("Could not find an editor for file %s"), reply->filename.c_str()); return; } m_activeEditor = editor; // What should we do with the TU? switch(reply->context) { case CTX_CachePCH: // Nothing more to be done return; default: break; } if(!reply->results && !reply->errorMessage.IsEmpty()) { // Notify about this error clCommandEvent event(wxEVT_CLANG_CODE_COMPLETE_MESSAGE); event.SetString(reply->errorMessage); event.SetInt(1); // indicates that this is an error message EventNotifier::Get()->AddPendingEvent(event); return; } if(m_activeEditor->GetCurrentPosition() < m_position) { CL_DEBUG(wxT("Current position is lower than the starting position, ignoring completion")); clang_disposeCodeCompleteResults(reply->results); return; } wxString typedString; if(m_activeEditor->GetCurrentPosition() > m_position) { // User kept on typing while the completion thread was working typedString = m_activeEditor->GetTextRange(m_position, m_activeEditor->GetCurrentPosition()); if(typedString.find_first_not_of(wxT("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_")) != wxString::npos) { // User typed some non valid identifier char, cancel code completion CL_DEBUG( wxT("User typed: %s since the completion thread started working until it ended, ignoring completion"), typedString.c_str()); clang_disposeCodeCompleteResults(reply->results); return; } } // update the filter word reply->filterWord.Append(typedString); CL_DEBUG(wxT("clang completion: filter word is %s"), reply->filterWord.c_str()); // For the Calltip, remove the opening brace from the filter string wxString filterWord = reply->filterWord; if(GetContext() == CTX_Calltip && filterWord.EndsWith(wxT("("))) filterWord.RemoveLast(); wxString lowerCaseFilter = filterWord; lowerCaseFilter.MakeLower(); unsigned numResults = reply->results->NumResults; clang_sortCodeCompletionResults(reply->results->Results, reply->results->NumResults); std::vector<TagEntryPtr> tags; for(unsigned i = 0; i < numResults; i++) { CXCompletionResult result = reply->results->Results[i]; CXCompletionString str = result.CompletionString; CXCursorKind kind = result.CursorKind; if(kind == CXCursor_NotImplemented) continue; wxString entryName, entrySignature, entryPattern, entryReturnValue; DoParseCompletionString(str, 0, entryName, entrySignature, entryPattern, entryReturnValue); wxString lowerCaseName = entryName; lowerCaseName.MakeLower(); if(!lowerCaseFilter.IsEmpty() && !lowerCaseName.StartsWith(lowerCaseFilter)) continue; if(clang_getCompletionAvailability(str) != CXAvailability_Available) continue; TagEntry* t = new TagEntry(); TagEntryPtr tag(t); tag->SetIsClangTag(true); tag->SetName(entryName); tag->SetPattern(entryPattern); tag->SetSignature(entrySignature); // Add support for clang comment parsing #if HAS_LIBCLANG_BRIEFCOMMENTS CXString BriefComment = clang_getCompletionBriefComment(str); const char* comment = clang_getCString(BriefComment); if(comment && comment[0] != '\0') { tag->SetComment(wxString(comment, wxConvUTF8)); } clang_disposeString(BriefComment); #endif switch(kind) { case CXCursor_EnumConstantDecl: tag->SetKind(wxT("enumerator")); break; case CXCursor_EnumDecl: tag->SetKind(wxT("enum")); break; case CXCursor_CXXMethod: case CXCursor_Constructor: case CXCursor_Destructor: case CXCursor_FunctionDecl: case CXCursor_FunctionTemplate: tag->SetKind(wxT("prototype")); break; case CXCursor_MacroDefinition: tag->SetKind(wxT("macro")); break; case CXCursor_Namespace: tag->SetKind(wxT("namespace")); break; case CXCursor_ClassDecl: case CXCursor_ClassTemplate: case CXCursor_ClassTemplatePartialSpecialization: tag->SetKind(wxT("class")); break; case CXCursor_StructDecl: tag->SetKind(wxT("struct")); break; case CXCursor_TypeRef: case CXCursor_TypedefDecl: tag->SetKind(wxT("typedef")); tag->SetKind(wxT("typedef")); break; default: tag->SetKind(wxT("variable")); break; } tags.push_back(tag); } clang_disposeCodeCompleteResults(reply->results); CL_DEBUG(wxT("Building completion results... done ")); if(GetContext() == CTX_Calltip) { std::vector<TagEntryPtr> tips; TagsManagerST::Get()->GetFunctionTipFromTags(tags, filterWord, tips); m_activeEditor->ShowCalltip(new clCallTip(tips)); } else { wxCodeCompletionBoxManager::Get().ShowCompletionBox( m_activeEditor->GetCtrl(), tags, wxCodeCompletionBox::kNone, wxNOT_FOUND); } }