mirror of
https://github.com/MaskRay/ccls.git
synced 2025-01-19 12:05:50 +00:00
Real-time diagnostics via clang_codeCompleteAt
This commit is contained in:
parent
54fed027ce
commit
b3d5327342
53
src/clang_utils.cc
Normal file
53
src/clang_utils.cc
Normal 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
12
src/clang_utils.h
Normal 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);
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user