From b3d53273422644ec177a16a66611c02c757f5b19 Mon Sep 17 00:00:00 2001 From: Jacob Dufault Date: Fri, 19 May 2017 23:35:14 -0700 Subject: [PATCH] Real-time diagnostics via clang_codeCompleteAt --- src/clang_utils.cc | 53 ++++++++++++++++++++++++++++++++++++++++++ src/clang_utils.h | 12 ++++++++++ src/code_completion.cc | 26 +++++++++++++++------ src/code_completion.h | 2 +- src/command_line.cc | 25 +++++++++++++------- src/indexer.cc | 39 ++++--------------------------- 6 files changed, 105 insertions(+), 52 deletions(-) create mode 100644 src/clang_utils.cc create mode 100644 src/clang_utils.h diff --git a/src/clang_utils.cc b/src/clang_utils.cc new file mode 100644 index 00000000..61e54cb1 --- /dev/null +++ b/src/clang_utils.cc @@ -0,0 +1,53 @@ +#include "clang_utils.h" + +#include "libclangmm/Utility.h" + +optional BuildDiagnostic(CXDiagnostic diagnostic) { + // Skip diagnostics in system headers. + CXSourceLocation diag_loc = clang_getDiagnosticLocation(diagnostic); + if (clang_Location_isInSystemHeader(diag_loc)) + return nullopt; + + // Get db so we can attribute diagnostic to the right indexed file. + CXFile file; + unsigned int line, column; + clang_getSpellingLocation(diag_loc, &file, &line, &column, nullptr); + + // Build diagnostic. + + lsDiagnostic ls_diagnostic; + + // TODO: ls_diagnostic.range is lsRange, we have Range. We should only be + // storing Range types when inside the indexer so that index <-> buffer + // remapping logic is applied. + ls_diagnostic.range = lsRange(lsPosition(line - 1, column), lsPosition(line - 1, column)); + + ls_diagnostic.message = clang::ToString(clang_getDiagnosticSpelling(diagnostic)); + + // Append the flag that enables this diagnostic, ie, [-Wswitch] + std::string enabling_flag = clang::ToString(clang_getDiagnosticOption(diagnostic, nullptr)); + if (!enabling_flag.empty()) + ls_diagnostic.message += " [" + enabling_flag + "]"; + + ls_diagnostic.code = clang_getDiagnosticCategory(diagnostic); + + switch (clang_getDiagnosticSeverity(diagnostic)) { + case CXDiagnostic_Ignored: + case CXDiagnostic_Note: + ls_diagnostic.severity = lsDiagnosticSeverity::Information; + break; + case CXDiagnostic_Warning: + ls_diagnostic.severity = lsDiagnosticSeverity::Warning; + break; + case CXDiagnostic_Error: + case CXDiagnostic_Fatal: + ls_diagnostic.severity = lsDiagnosticSeverity::Error; + break; + } + + // TODO: integrate FixIts (ie, textDocument/codeAction) + + clang_disposeDiagnostic(diagnostic); + + return ls_diagnostic; +} diff --git a/src/clang_utils.h b/src/clang_utils.h new file mode 100644 index 00000000..b7b17d6f --- /dev/null +++ b/src/clang_utils.h @@ -0,0 +1,12 @@ +#pragma once + +#include "language_server_api.h" + +#include + +#include +#include + +using namespace std::experimental; + +optional BuildDiagnostic(CXDiagnostic diagnostic); \ No newline at end of file diff --git a/src/code_completion.cc b/src/code_completion.cc index 07cbf561..d703e50e 100644 --- a/src/code_completion.cc +++ b/src/code_completion.cc @@ -1,5 +1,6 @@ #include "code_completion.h" +#include "clang_utils.h" #include "libclangmm/Utility.h" #include "platform.h" #include "timer.h" @@ -276,8 +277,6 @@ void CompletionQueryMain(CompletionManager* completion_manager) { std::unique_ptr request = completion_manager->completion_request.Take(); - NonElidedVector ls_result; - CompletionSession* session = completion_manager->GetOrOpenSession(request->location.textDocument.uri.GetPath()); std::lock_guard lock(session->usage_lock); @@ -302,13 +301,14 @@ void CompletionQueryMain(CompletionManager* completion_manager) { CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeBriefComments); if (!cx_results) { std::cerr << "[complete] Code completion failed" << std::endl; - request->on_complete(ls_result); + request->on_complete({}, {}); continue; } timer.ResetAndPrint("[complete] clangCodeCompleteAt"); std::cerr << "[complete] Got " << cx_results->NumResults << " results" << std::endl; - + + NonElidedVector ls_result; ls_result.reserve(cx_results->NumResults); timer.Reset(); @@ -342,10 +342,22 @@ void CompletionQueryMain(CompletionManager* completion_manager) { } timer.ResetAndPrint("[complete] Building " + std::to_string(ls_result.size()) + " completion results"); - clang_disposeCodeCompleteResults(cx_results); - timer.ResetAndPrint("[complete] clang_disposeCodeCompleteResults "); + // Build diagnostics. + NonElidedVector ls_diagnostics; + timer.Reset(); + unsigned num_diagnostics = clang_codeCompleteGetNumDiagnostics(cx_results); + for (unsigned i = 0; i < num_diagnostics; ++i) { + optional diagnostic = BuildDiagnostic(clang_codeCompleteGetDiagnostic(cx_results, i)); + if (diagnostic) + ls_diagnostics.push_back(*diagnostic); + } + timer.ResetAndPrint("[complete] Build diagnostics"); + + clang_disposeCodeCompleteResults(cx_results); + timer.ResetAndPrint("[complete] clang_disposeCodeCompleteResults"); + + request->on_complete(ls_result, ls_diagnostics); - request->on_complete(ls_result); continue; } } diff --git a/src/code_completion.h b/src/code_completion.h index 6c1c4fa5..e87a7e45 100644 --- a/src/code_completion.h +++ b/src/code_completion.h @@ -39,7 +39,7 @@ struct CompletionManager { Project* project; WorkingFiles* working_files; - using OnComplete = std::function results)>; + using OnComplete = std::function results, NonElidedVector diagnostics)>; struct CompletionRequest { lsTextDocumentPositionParams location; diff --git a/src/command_line.cc b/src/command_line.cc index df3ca187..b375b704 100644 --- a/src/command_line.cc +++ b/src/command_line.cc @@ -1691,21 +1691,28 @@ bool QueryDbMainLoop( auto msg = static_cast(message.get()); lsTextDocumentPositionParams params = msg->params; - CompletionManager::OnComplete callback = std::bind([](BaseIpcMessage* message, const NonElidedVector& results) { + CompletionManager::OnComplete callback = std::bind([working_files](BaseIpcMessage* message, NonElidedVector results, NonElidedVector diagnostics) { auto msg = static_cast(message); auto ipc = IpcManager::instance(); - Out_TextDocumentComplete response; - response.id = msg->id; - response.result.isIncomplete = false; - response.result.items = results; + Out_TextDocumentComplete complete_response; + complete_response.id = msg->id; + complete_response.result.isIncomplete = false; + complete_response.result.items = results; - Timer timer; - ipc->SendOutMessageToClient(IpcId::TextDocumentCompletion, response); - timer.ResetAndPrint("[complete] Writing completion results"); + ipc->SendOutMessageToClient(IpcId::TextDocumentCompletion, complete_response); + + Out_TextDocumentPublishDiagnostics diagnostic_response; + diagnostic_response.params.uri = msg->params.textDocument.uri; + diagnostic_response.params.diagnostics = diagnostics; + ipc->SendOutMessageToClient(IpcId::TextDocumentPublishDiagnostics, diagnostic_response); + + WorkingFile* working_file = working_files->GetFileByFilename(msg->params.textDocument.uri.GetPath()); + if (working_file) + working_file->has_diagnostics = !diagnostics.empty(); delete message; - }, message.release(), std::placeholders::_1); + }, message.release(), std::placeholders::_1, std::placeholders::_2); completion_manager->CodeComplete(params, std::move(callback)); diff --git a/src/indexer.cc b/src/indexer.cc index d5f66a4b..19b0e9d1 100644 --- a/src/indexer.cc +++ b/src/indexer.cc @@ -1,5 +1,6 @@ #include "indexer.h" +#include "clang_utils.h" #include "libclangmm/Cursor.h" #include "libclangmm/Index.h" #include "libclangmm/TranslationUnit.h" @@ -240,41 +241,9 @@ void diagnostic(CXClientData client_data, // Build diagnostic. - lsDiagnostic ls_diagnostic; - - // TODO: ls_diagnostic.range is lsRange, we have Range. We should only be - // storing Range types when inside the indexer so that index <-> buffer - // remapping logic is applied. - ls_diagnostic.range = lsRange(lsPosition(line - 1, column), lsPosition(line - 1, column)); - - ls_diagnostic.message = clang::ToString(clang_getDiagnosticSpelling(diagnostic)); - - // Append the flag that enables this diagnostic, ie, [-Wswitch] - std::string enabling_flag = clang::ToString(clang_getDiagnosticOption(diagnostic, nullptr)); - if (!enabling_flag.empty()) - ls_diagnostic.message += " [" + enabling_flag + "]"; - - ls_diagnostic.code = clang_getDiagnosticCategory(diagnostic); - - switch (clang_getDiagnosticSeverity(diagnostic)) { - case CXDiagnostic_Ignored: - case CXDiagnostic_Note: - ls_diagnostic.severity = lsDiagnosticSeverity::Information; - break; - case CXDiagnostic_Warning: - ls_diagnostic.severity = lsDiagnosticSeverity::Warning; - break; - case CXDiagnostic_Error: - case CXDiagnostic_Fatal: - ls_diagnostic.severity = lsDiagnosticSeverity::Error; - break; - } - - // TODO: integrate FixIts (ie, textDocument/codeAction) - - clang_disposeDiagnostic(diagnostic); - - db->diagnostics.push_back(ls_diagnostic); + optional ls_diagnostic = BuildDiagnostic(diagnostic); + if (ls_diagnostic) + db->diagnostics.push_back(*ls_diagnostic); } }