From 0e798944222bd58336babbf7fbb704bb00ef3ae8 Mon Sep 17 00:00:00 2001 From: eidheim Date: Fri, 14 Aug 2026 18:20:09 +0200 Subject: [PATCH] Language client: added support for textDocument/diagnostic --- src/source_language_protocol.cpp | 108 ++++++++++++++++++++----------- src/source_language_protocol.hpp | 4 ++ 2 files changed, 75 insertions(+), 37 deletions(-) diff --git a/src/source_language_protocol.cpp b/src/source_language_protocol.cpp index 785f3fc..6e45b3d 100644 --- a/src/source_language_protocol.cpp +++ b/src/source_language_protocol.cpp @@ -277,7 +277,8 @@ LanguageProtocol::Capabilities LanguageProtocol::Client::initialize() { "formatting": { "dynamicRegistration": false }, "rangeFormatting": { "dynamicRegistration": false }, "rename": { "dynamicRegistration": false }, - "publishDiagnostics": { "relatedInformation":true } + "publishDiagnostics": { "relatedInformation": true }, + "diagnostic": { "dynamicRegistration": false, "relatedDocumentSupport": true } } }, "initializationOptions": { @@ -340,6 +341,8 @@ LanguageProtocol::Capabilities LanguageProtocol::Client::initialize() { capabilities.workspace_folders = change_notifications->boolean_or(true); // Can be string as well } } + // Workaround since rust-analyzer erroneously uses both publish and pull diagnostic + capabilities.pull_diagnostic = language_id == "rust" ? false : boolean_or_object("diagnosticProvider"); } // See https://clangd.llvm.org/extensions.html#utf-8-offsets for documentation on offsetEncoding @@ -743,7 +746,10 @@ void Source::LanguageProtocolView::initialize() { update_status_state(this); } - update_type_coverage(); + if(capabilities.type_coverage) + update_type_coverage(); + if(capabilities.pull_diagnostic) + pull_diagnostic(); initialized = true; }; @@ -765,6 +771,7 @@ Source::LanguageProtocolView::~LanguageProtocolView() { autocomplete_delayed_show_arguments_connection.disconnect(); resolve_completion_item_connection.disconnect(); update_type_coverage_connection.disconnect(); + pull_diagnostic_connection.disconnect(); if(initialize_thread.joinable()) initialize_thread.join(); @@ -878,7 +885,8 @@ bool Source::LanguageProtocolView::save() { write_notification("textDocument/didSave"); - update_type_coverage(); + if(capabilities.type_coverage) + update_type_coverage(); return true; } @@ -1545,6 +1553,12 @@ void Source::LanguageProtocolView::setup_signals() { write_did_change_notification({{"contentChanges", "[{" + to_string({"text", '"' + JSON::escape_string(get_buffer()->get_text().raw()) + '"'}) + "}]"}}); }); } + + if(capabilities.pull_diagnostic) { + get_buffer()->signal_changed().connect([this]() { + pull_diagnostic(); + }); + } } void Source::LanguageProtocolView::setup_autocomplete() { @@ -2526,44 +2540,64 @@ boost::optional Source::LanguageProtocolView::get_named_parameter_symbol() } void Source::LanguageProtocolView::update_type_coverage() { - if(capabilities.type_coverage) { - write_request("textDocument/typeCoverage", {}, [this](JSON &&result, bool error) { - if(error) { - if(update_type_coverage_retries > 0) { // Retry typeCoverage request, since these requests can fail while waiting for language server to start - dispatcher.post([this] { - update_type_coverage_connection.disconnect(); - update_type_coverage_connection = Glib::signal_timeout().connect( - [this]() { - --update_type_coverage_retries; - update_type_coverage(); - return false; - }, - 1000); - }); - } - return; + write_request("textDocument/typeCoverage", {}, [this](JSON &&result, bool error) { + if(error) { + if(update_type_coverage_retries > 0) { // Retry typeCoverage request, since these requests can fail while waiting for language server to start + dispatcher.post([this] { + update_type_coverage_connection.disconnect(); + update_type_coverage_connection = Glib::signal_timeout().connect( + [this]() { + --update_type_coverage_retries; + update_type_coverage(); + return false; + }, + 1000); + }); } - update_type_coverage_retries = 0; + return; + } + update_type_coverage_retries = 0; - std::vector ranges; - for(auto &uncovered_range : result.array_or_empty("uncoveredRanges")) { - try { - ranges.emplace_back(uncovered_range.object("range")); - } - catch(...) { - } + std::vector ranges; + for(auto &uncovered_range : result.array_or_empty("uncoveredRanges")) { + try { + ranges.emplace_back(uncovered_range.object("range")); } + catch(...) { + } + } - dispatcher.post([this, ranges = std::move(ranges)] { - type_coverage_marks.clear(); - for(auto &range : ranges) { - auto start = get_iter_at_line_pos(range.start.line, range.start.character); - auto end = get_iter_at_line_pos(range.end.line, range.end.character); - type_coverage_marks.emplace_back(start, end); - } + dispatcher.post([this, ranges = std::move(ranges)] { + type_coverage_marks.clear(); + for(auto &range : ranges) { + auto start = get_iter_at_line_pos(range.start.line, range.start.character); + auto end = get_iter_at_line_pos(range.end.line, range.end.character); + type_coverage_marks.emplace_back(start, end); + } - update_diagnostics(last_diagnostics); - }); + update_diagnostics(last_diagnostics); }); - } + }); +} + +void Source::LanguageProtocolView::pull_diagnostic() { + pull_diagnostic_connection.disconnect(); + pull_diagnostic_connection = Glib::signal_timeout().connect( + [this]() { + write_request("textDocument/diagnostic", {}, [this](JSON &&result, bool error) { + if(!error) { + std::vector> diagnostics; + for(auto &child : result.array_or_empty("items")) { + try { + diagnostics.emplace_back(std::make_shared(std::move(child))); + } + catch(...) { + } + } + update_diagnostics_async(std::move(diagnostics)); + } + }); + return false; + }, + 1000); } diff --git a/src/source_language_protocol.hpp b/src/source_language_protocol.hpp index 4d9cc93..ff2feab 100644 --- a/src/source_language_protocol.hpp +++ b/src/source_language_protocol.hpp @@ -140,6 +140,7 @@ namespace LanguageProtocol { bool code_action_resolve = false; bool execute_command = false; bool type_coverage = false; + bool pull_diagnostic = false; bool use_line_index = false; }; @@ -307,5 +308,8 @@ namespace Source { size_t num_warnings = 0, num_errors = 0, num_fix_its = 0; void update_type_coverage(); std::atomic update_type_coverage_retries = {60}; + + sigc::connection pull_diagnostic_connection; + void pull_diagnostic(); }; } // namespace Source