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());
    }
}
示例#3
0
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);
    }
}