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 "code_completion.h"
|
||||||
|
|
||||||
|
#include "clang_utils.h"
|
||||||
#include "libclangmm/Utility.h"
|
#include "libclangmm/Utility.h"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
@ -276,8 +277,6 @@ void CompletionQueryMain(CompletionManager* completion_manager) {
|
|||||||
std::unique_ptr<CompletionManager::CompletionRequest> request = completion_manager->completion_request.Take();
|
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());
|
CompletionSession* session = completion_manager->GetOrOpenSession(request->location.textDocument.uri.GetPath());
|
||||||
std::lock_guard<std::mutex> lock(session->usage_lock);
|
std::lock_guard<std::mutex> lock(session->usage_lock);
|
||||||
|
|
||||||
@ -302,13 +301,14 @@ void CompletionQueryMain(CompletionManager* completion_manager) {
|
|||||||
CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeBriefComments);
|
CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeBriefComments);
|
||||||
if (!cx_results) {
|
if (!cx_results) {
|
||||||
std::cerr << "[complete] Code completion failed" << std::endl;
|
std::cerr << "[complete] Code completion failed" << std::endl;
|
||||||
request->on_complete(ls_result);
|
request->on_complete({}, {});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
timer.ResetAndPrint("[complete] clangCodeCompleteAt");
|
timer.ResetAndPrint("[complete] clangCodeCompleteAt");
|
||||||
std::cerr << "[complete] Got " << cx_results->NumResults << " results" << std::endl;
|
std::cerr << "[complete] Got " << cx_results->NumResults << " results" << std::endl;
|
||||||
|
|
||||||
|
NonElidedVector<lsCompletionItem> ls_result;
|
||||||
ls_result.reserve(cx_results->NumResults);
|
ls_result.reserve(cx_results->NumResults);
|
||||||
|
|
||||||
timer.Reset();
|
timer.Reset();
|
||||||
@ -342,10 +342,22 @@ void CompletionQueryMain(CompletionManager* completion_manager) {
|
|||||||
}
|
}
|
||||||
timer.ResetAndPrint("[complete] Building " + std::to_string(ls_result.size()) + " completion results");
|
timer.ResetAndPrint("[complete] Building " + std::to_string(ls_result.size()) + " completion results");
|
||||||
|
|
||||||
clang_disposeCodeCompleteResults(cx_results);
|
// Build diagnostics.
|
||||||
timer.ResetAndPrint("[complete] clang_disposeCodeCompleteResults ");
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ struct CompletionManager {
|
|||||||
Project* project;
|
Project* project;
|
||||||
WorkingFiles* working_files;
|
WorkingFiles* working_files;
|
||||||
|
|
||||||
using OnComplete = std::function<void(NonElidedVector<lsCompletionItem> results)>;
|
using OnComplete = std::function<void(NonElidedVector<lsCompletionItem> results, NonElidedVector<lsDiagnostic> diagnostics)>;
|
||||||
|
|
||||||
struct CompletionRequest {
|
struct CompletionRequest {
|
||||||
lsTextDocumentPositionParams location;
|
lsTextDocumentPositionParams location;
|
||||||
|
@ -1691,21 +1691,28 @@ bool QueryDbMainLoop(
|
|||||||
auto msg = static_cast<Ipc_TextDocumentComplete*>(message.get());
|
auto msg = static_cast<Ipc_TextDocumentComplete*>(message.get());
|
||||||
lsTextDocumentPositionParams params = msg->params;
|
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 msg = static_cast<Ipc_TextDocumentComplete*>(message);
|
||||||
auto ipc = IpcManager::instance();
|
auto ipc = IpcManager::instance();
|
||||||
|
|
||||||
Out_TextDocumentComplete response;
|
Out_TextDocumentComplete complete_response;
|
||||||
response.id = msg->id;
|
complete_response.id = msg->id;
|
||||||
response.result.isIncomplete = false;
|
complete_response.result.isIncomplete = false;
|
||||||
response.result.items = results;
|
complete_response.result.items = results;
|
||||||
|
|
||||||
Timer timer;
|
ipc->SendOutMessageToClient(IpcId::TextDocumentCompletion, complete_response);
|
||||||
ipc->SendOutMessageToClient(IpcId::TextDocumentCompletion, response);
|
|
||||||
timer.ResetAndPrint("[complete] Writing completion results");
|
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;
|
delete message;
|
||||||
}, message.release(), std::placeholders::_1);
|
}, message.release(), std::placeholders::_1, std::placeholders::_2);
|
||||||
|
|
||||||
completion_manager->CodeComplete(params, std::move(callback));
|
completion_manager->CodeComplete(params, std::move(callback));
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "indexer.h"
|
#include "indexer.h"
|
||||||
|
|
||||||
|
#include "clang_utils.h"
|
||||||
#include "libclangmm/Cursor.h"
|
#include "libclangmm/Cursor.h"
|
||||||
#include "libclangmm/Index.h"
|
#include "libclangmm/Index.h"
|
||||||
#include "libclangmm/TranslationUnit.h"
|
#include "libclangmm/TranslationUnit.h"
|
||||||
@ -240,41 +241,9 @@ void diagnostic(CXClientData client_data,
|
|||||||
|
|
||||||
// Build diagnostic.
|
// Build diagnostic.
|
||||||
|
|
||||||
lsDiagnostic ls_diagnostic;
|
optional<lsDiagnostic> ls_diagnostic = BuildDiagnostic(diagnostic);
|
||||||
|
if (ls_diagnostic)
|
||||||
// TODO: ls_diagnostic.range is lsRange, we have Range. We should only be
|
db->diagnostics.push_back(*ls_diagnostic);
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user