Real-time diagnostics via clang_codeCompleteAt

This commit is contained in:
Jacob Dufault 2017-05-19 23:35:14 -07:00
parent 54fed027ce
commit b3d5327342
6 changed files with 105 additions and 52 deletions

53
src/clang_utils.cc Normal file
View File

@ -0,0 +1,53 @@
#include "clang_utils.h"
#include "libclangmm/Utility.h"
optional<lsDiagnostic> 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;
}

12
src/clang_utils.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include "language_server_api.h"
#include <clang-c/Index.h>
#include <vector>
#include <optional.h>
using namespace std::experimental;
optional<lsDiagnostic> BuildDiagnostic(CXDiagnostic diagnostic);

View File

@ -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<CompletionManager::CompletionRequest> request = completion_manager->completion_request.Take();
NonElidedVector<lsCompletionItem> ls_result;
CompletionSession* session = completion_manager->GetOrOpenSession(request->location.textDocument.uri.GetPath());
std::lock_guard<std::mutex> 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<lsCompletionItem> 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<lsDiagnostic> ls_diagnostics;
timer.Reset();
unsigned num_diagnostics = clang_codeCompleteGetNumDiagnostics(cx_results);
for (unsigned i = 0; i < num_diagnostics; ++i) {
optional<lsDiagnostic> 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;
}
}

View File

@ -39,7 +39,7 @@ struct CompletionManager {
Project* project;
WorkingFiles* working_files;
using OnComplete = std::function<void(NonElidedVector<lsCompletionItem> results)>;
using OnComplete = std::function<void(NonElidedVector<lsCompletionItem> results, NonElidedVector<lsDiagnostic> diagnostics)>;
struct CompletionRequest {
lsTextDocumentPositionParams location;

View File

@ -1691,21 +1691,28 @@ bool QueryDbMainLoop(
auto msg = static_cast<Ipc_TextDocumentComplete*>(message.get());
lsTextDocumentPositionParams params = msg->params;
CompletionManager::OnComplete callback = std::bind([](BaseIpcMessage* message, const NonElidedVector<lsCompletionItem>& results) {
CompletionManager::OnComplete callback = std::bind([working_files](BaseIpcMessage* message, NonElidedVector<lsCompletionItem> results, NonElidedVector<lsDiagnostic> diagnostics) {
auto msg = static_cast<Ipc_TextDocumentComplete*>(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));

View File

@ -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<lsDiagnostic> ls_diagnostic = BuildDiagnostic(diagnostic);
if (ls_diagnostic)
db->diagnostics.push_back(*ls_diagnostic);
}
}