void ClangUtils::printCompletionDiagnostics(CXCodeCompleteResults *res) { //// Report diagnostics to the log file const unsigned diagCount = clang_codeCompleteGetNumDiagnostics(res); for(unsigned i=0; i<diagCount; i++) { CXDiagnostic diag = clang_codeCompleteGetDiagnostic(res, i); CXString diagStr = clang_getDiagnosticSpelling(diag); wxString wxDiagString = wxString(clang_getCString(diagStr), wxConvUTF8); CL_DEBUG(wxT("Completion diagnostic [%d]: %s"), clang_getDiagnosticSeverity(diag), wxDiagString.c_str()); clang_disposeString(diagStr); clang_disposeDiagnostic(diag); } }
int perform_code_completion(int argc, const char **argv) { const char *input = argv[1]; char *filename = 0; unsigned line; unsigned column; CXIndex CIdx; int errorCode; struct CXUnsavedFile *unsaved_files = 0; int num_unsaved_files = 0; CXCodeCompleteResults *results = 0; input += strlen("-code-completion-at="); if ((errorCode = parse_file_line_column(input, &filename, &line, &column, 0, 0))) return errorCode; if (parse_remapped_files(argc, argv, 2, &unsaved_files, &num_unsaved_files)) 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 (results) { unsigned i, n = results->NumResults; for (i = 0; i != n; ++i) print_completion_result(results->Results + i, stdout); n = clang_codeCompleteGetNumDiagnostics(results); for (i = 0; i != n; ++i) { CXDiagnostic diag = clang_codeCompleteGetDiagnostic(results, i); PrintDiagnostic(diag); clang_disposeDiagnostic(diag); } clang_disposeCodeCompleteResults(results); } clang_disposeIndex(CIdx); free(filename); free_remapped_files(unsaved_files, num_unsaved_files); return 0; }
void GmacsPreprocessor::codeCompletion(const QTextCursor &cursor) { int line = cursor.blockNumber(); int column = cursor.columnNumber(); //const char *name = filename->toLocal8Bit().data(); const char *name = clang_getCString(clang_getTranslationUnitSpelling(unit)); //qDebug() << cursor.document()->toPlainText(); //qDebug() << cursor.document()->toPlainText().size(); QString document = cursor.document()->toPlainText(); QString text = cursor.block().text(); QRegExp exp("\t"); int tab_count = text.count(exp); fprintf(stderr, "column = [%d]\n", cursor.positionInBlock()); //unsaved_file->Filename = name; //unsaved_file->Contents = document.toLocal8Bit().data(); //unsaved_file->Length = document.size(); if (!unit) return; CLANG_REPARSE(unit, NULL);//unsaved_file); fprintf(stderr, "line = [%d], column = [%d]\n", line+1, column + tab_count); fprintf(stderr, "name = [%s]\n", name); CXCodeCompleteResults *res = CLANG_CODE_COMPLETION(unit, name, line+1, column + tab_count); if (!res) fprintf(stderr, "ERROR: could not complete\n"); for (size_t i = 0; i < clang_codeCompleteGetNumDiagnostics(res); i++) { const CXDiagnostic &diag = clang_codeCompleteGetDiagnostic(res, i); const CXString &s = clang_getDiagnosticSpelling(diag); fprintf(stderr, "%s\n", clang_getCString(s)); } unsigned num_results = res->NumResults; fprintf(stderr, "num_results = [%d]\n"); for (unsigned i = 0; i < num_results; i++) { const CXCompletionString& str = res->Results[i].CompletionString; unsigned chunks = clang_getNumCompletionChunks(str); for (unsigned j = 0; j < chunks; j++) { const CXString& out = clang_getCompletionChunkText(str, j); //std::cout << clang_getCString(out) << " "; if (clang_getCompletionChunkKind(str, j) != CXCompletionChunk_TypedText) continue; } //std::cout << std::endl; } clang_disposeCodeCompleteResults(res); }
void CompletionJob::processDiagnostics(CXCodeCompleteResults* results) { if (!testLog(CompilationError)) return; const unsigned int numDiags = clang_codeCompleteGetNumDiagnostics(results); for (unsigned int curDiag = 0; curDiag < numDiags; ++curDiag) { CXDiagnostic diagnostic = clang_codeCompleteGetDiagnostic(results, curDiag); const unsigned diagnosticOptions = (CXDiagnostic_DisplaySourceLocation| CXDiagnostic_DisplayColumn| CXDiagnostic_DisplaySourceRanges| CXDiagnostic_DisplayOption| CXDiagnostic_DisplayCategoryId| CXDiagnostic_DisplayCategoryName); const ByteArray text = RTags::eatString(clang_formatDiagnostic(diagnostic, diagnosticOptions)); log(CompilationError, "%s", text.constData()); clang_disposeDiagnostic(diagnostic); } log(CompilationError, "$"); }
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"; } }
diagnostic code_complete_results::getDiagnostic(unsigned idx) { return { clang_codeCompleteGetDiagnostic(results, idx) }; }
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()); } }
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; }