Browse Source

Language client: now adds diagnostics to codeAction requests, and supports adding configuration responses. Also now uses buffer completions when completion is not provided by server.

merge-requests/413/head
eidheim 3 years ago
parent
commit
e9044e386b
  1. 99
      src/source_generic.cpp
  2. 8
      src/source_generic.hpp
  3. 131
      src/source_language_protocol.cpp
  4. 13
      src/source_language_protocol.hpp

99
src/source_generic.cpp

@ -8,37 +8,9 @@
#include <algorithm>
#include <boost/algorithm/string.hpp>
Source::GenericView::GenericView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language) : BaseView(file_path, language), View(file_path, language, true), autocomplete(this, interactive_completion, last_keyval, false, false) {
if(language) {
auto language_manager = LanguageManager::get_default();
auto search_paths = language_manager->get_search_path();
bool found_language_file = false;
boost::filesystem::path language_file;
boost::system::error_code ec;
for(auto &search_path : search_paths) {
boost::filesystem::path p(static_cast<std::string>(search_path) + '/' + static_cast<std::string>(language->get_id()) + ".lang");
if(boost::filesystem::exists(p, ec) && boost::filesystem::is_regular_file(p, ec)) {
language_file = p;
found_language_file = true;
break;
}
}
if(found_language_file) {
boost::property_tree::ptree pt;
try {
boost::property_tree::xml_parser::read_xml(language_file.string(), pt);
parse_language_file(pt);
}
catch(const std::exception &e) {
Terminal::get().print("\e[31mError\e[m: error parsing language file " + filesystem::get_short_path(language_file).string() + ": " + e.what() + '\n', true);
}
}
}
setup_buffer_words();
setup_autocomplete();
Source::GenericView::GenericView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language, bool is_generic_view) : BaseView(file_path, language), View(file_path, language, is_generic_view) {
if(is_generic_view)
setup_autocomplete();
}
void Source::GenericView::parse_language_file(const boost::property_tree::ptree &pt) {
@ -171,13 +143,44 @@ void Source::GenericView::setup_buffer_words() {
}
void Source::GenericView::setup_autocomplete() {
autocomplete = std::make_unique<Autocomplete>(this, interactive_completion, last_keyval, false, false);
if(language) {
auto language_manager = LanguageManager::get_default();
auto search_paths = language_manager->get_search_path();
bool found_language_file = false;
boost::filesystem::path language_file;
boost::system::error_code ec;
for(auto &search_path : search_paths) {
boost::filesystem::path path(static_cast<std::string>(search_path) + '/' + static_cast<std::string>(language->get_id()) + ".lang");
if(boost::filesystem::exists(path, ec) && boost::filesystem::is_regular_file(path, ec)) {
language_file = path;
found_language_file = true;
break;
}
}
if(found_language_file) {
boost::property_tree::ptree pt;
try {
boost::property_tree::xml_parser::read_xml(language_file.string(), pt);
parse_language_file(pt);
}
catch(const std::exception &e) {
Terminal::get().print("\e[31mError\e[m: error parsing language file " + filesystem::get_short_path(language_file).string() + ": " + e.what() + '\n', true);
}
}
}
setup_buffer_words();
non_interactive_completion = [this] {
if(CompletionDialog::get() && CompletionDialog::get()->is_visible())
return;
autocomplete.run();
autocomplete->run();
};
autocomplete.run_check = [this]() {
autocomplete->run_check = [this]() {
auto prefix_start = get_buffer()->get_insert()->get_iter();
auto prefix_end = prefix_start;
@ -189,13 +192,13 @@ void Source::GenericView::setup_autocomplete() {
prefix_start.forward_char();
if((count >= 3 && !(*prefix_start >= '0' && *prefix_start <= '9')) || !interactive_completion) {
LockGuard lock(autocomplete.prefix_mutex);
autocomplete.prefix = get_buffer()->get_text(prefix_start, prefix_end);
LockGuard lock(autocomplete->prefix_mutex);
autocomplete->prefix = get_buffer()->get_text(prefix_start, prefix_end);
if(interactive_completion)
show_prefix_buffer_word = buffer_words.find(autocomplete.prefix) != buffer_words.end();
show_prefix_buffer_word = buffer_words.find(autocomplete->prefix) != buffer_words.end();
else {
auto it = buffer_words.find(autocomplete.prefix);
auto it = buffer_words.find(autocomplete->prefix);
show_prefix_buffer_word = !(it == buffer_words.end() || it->second == 1);
}
return true;
@ -204,19 +207,19 @@ void Source::GenericView::setup_autocomplete() {
return false;
};
autocomplete.add_rows = [this](std::string & /*buffer*/, int /*line*/, int /*line_index*/) {
if(autocomplete.state == Autocomplete::State::starting) {
autocomplete->add_rows = [this](std::string & /*buffer*/, int /*line*/, int /*line_index*/) {
if(autocomplete->state == Autocomplete::State::starting) {
autocomplete_comment.clear();
autocomplete_insert.clear();
std::string prefix;
{
LockGuard lock(autocomplete.prefix_mutex);
prefix = autocomplete.prefix;
LockGuard lock(autocomplete->prefix_mutex);
prefix = autocomplete->prefix;
}
for(auto &keyword : keywords) {
if(starts_with(keyword, prefix)) {
autocomplete.rows.emplace_back(keyword);
autocomplete->rows.emplace_back(keyword);
autocomplete_insert.emplace_back(keyword);
autocomplete_comment.emplace_back("");
}
@ -226,7 +229,7 @@ void Source::GenericView::setup_autocomplete() {
if((show_prefix_buffer_word || buffer_word.first.size() > prefix.size()) &&
starts_with(buffer_word.first, prefix) &&
keywords.find(buffer_word.first) == keywords.end()) {
autocomplete.rows.emplace_back(buffer_word.first);
autocomplete->rows.emplace_back(buffer_word.first);
auto insert = buffer_word.first;
boost::replace_all(insert, "$", "\\$");
autocomplete_insert.emplace_back(insert);
@ -238,7 +241,7 @@ void Source::GenericView::setup_autocomplete() {
if(snippets) {
for(auto &snippet : *snippets) {
if(starts_with(snippet.prefix, prefix)) {
autocomplete.rows.emplace_back(snippet.prefix);
autocomplete->rows.emplace_back(snippet.prefix);
autocomplete_insert.emplace_back(snippet.body);
autocomplete_comment.emplace_back(snippet.description);
}
@ -250,16 +253,16 @@ void Source::GenericView::setup_autocomplete() {
return true;
};
autocomplete.on_show = [this] {
autocomplete->on_show = [this] {
hide_tooltips();
};
autocomplete.on_hide = [this] {
autocomplete->on_hide = [this] {
autocomplete_comment.clear();
autocomplete_insert.clear();
};
autocomplete.on_select = [this](unsigned int index, const std::string &text, bool hide_window) {
autocomplete->on_select = [this](unsigned int index, const std::string &text, bool hide_window) {
get_buffer()->erase(CompletionDialog::get()->start_mark->get_iter(), get_buffer()->get_insert()->get_iter());
if(hide_window)
@ -268,7 +271,7 @@ void Source::GenericView::setup_autocomplete() {
get_buffer()->insert(CompletionDialog::get()->start_mark->get_iter(), text);
};
autocomplete.set_tooltip_buffer = [this](unsigned int index) -> std::function<void(Tooltip & tooltip)> {
autocomplete->set_tooltip_buffer = [this](unsigned int index) -> std::function<void(Tooltip & tooltip)> {
auto tooltip_str = autocomplete_comment[index];
if(tooltip_str.empty())
return nullptr;

8
src/source_generic.hpp

@ -7,7 +7,10 @@
namespace Source {
class GenericView : public View {
public:
GenericView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language);
GenericView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language, bool is_generic_view = true);
protected:
void setup_autocomplete();
private:
void parse_language_file(const boost::property_tree::ptree &pt);
@ -20,9 +23,8 @@ namespace Source {
bool show_prefix_buffer_word = false; /// To avoid showing the current word if it is unique in document
void setup_buffer_words();
Autocomplete autocomplete;
std::unique_ptr<Autocomplete> autocomplete;
std::vector<std::string> autocomplete_comment;
std::vector<std::string> autocomplete_insert;
void setup_autocomplete();
};
} // namespace Source

131
src/source_language_protocol.cpp

@ -52,7 +52,7 @@ LanguageProtocol::Diagnostic::RelatedInformation::RelatedInformation(const JSON
LanguageProtocol::Diagnostic::Diagnostic(JSON &&diagnostic) : message(diagnostic.string("message")), range(diagnostic.object("range")), severity(diagnostic.integer_or("severity", 0)), code(diagnostic.string_or("code", "")) {
for(auto &related_information : diagnostic.array_or_empty("relatedInformation"))
related_informations.emplace_back(related_information);
object = std::make_shared<JSON>(JSON::make_owner(std::move(diagnostic)));
object = std::make_unique<JSON>(JSON::make_owner(std::move(diagnostic)));
}
LanguageProtocol::TextEdit::TextEdit(const JSON &text_edit, std::string new_text_) : range(text_edit.object("range")), new_text(new_text_.empty() ? text_edit.string("newText") : std::move(new_text_)) {}
@ -222,7 +222,8 @@ LanguageProtocol::Capabilities LanguageProtocol::Client::initialize() {
"workspace": {
"symbol": { "dynamicRegistration": false },
"executeCommand": { "dynamicRegistration": false },
"workspaceFolders": true
"workspaceFolders": true,
"configuration": true
},
"textDocument": {
"synchronization": { "dynamicRegistration": false, "didSave": true },
@ -292,8 +293,21 @@ LanguageProtocol::Capabilities LanguageProtocol::Client::initialize() {
capabilities.text_document_sync = static_cast<LanguageProtocol::Capabilities::TextDocumentSync>(child->integer_or("change", 0));
}
if(auto child = object->child_optional("completionProvider")) {
capabilities.completion = true;
capabilities.completion_resolve = child->boolean_or("resolveProvider", false);
bool is_ltex = false; // Workaround for https://github.com/valentjn/ltex-ls that erroneously reports completionProvider
try {
for(auto &command : object->child("executeCommandProvider").array("commands")) {
if(starts_with(command.string(), "_ltex.")) {
is_ltex = true;
break;
}
}
}
catch(...) {
}
if(!is_ltex) {
capabilities.completion = true;
capabilities.completion_resolve = child->boolean_or("resolveProvider", false);
}
}
/// Some server capabilities are reported as either boolean or objects
auto boolean_or_object = [&object](const std::string &provider) {
@ -540,12 +554,12 @@ void LanguageProtocol::Client::write_notification(const std::string &method, con
void LanguageProtocol::Client::handle_server_notification(const std::string &method, JSON &&params) {
if(method == "textDocument/publishDiagnostics") {
std::vector<Diagnostic> diagnostics;
std::vector<std::shared_ptr<Diagnostic>> diagnostics;
auto file = filesystem::get_path_from_uri(params.string_or("uri", ""));
if(!file.empty()) {
for(auto &child : params.array_or_empty("diagnostics")) {
try {
diagnostics.emplace_back(std::move(child));
diagnostics.emplace_back(std::make_shared<Diagnostic>(std::move(child)));
}
catch(...) {
}
@ -661,12 +675,32 @@ void LanguageProtocol::Client::handle_server_request(const boost::variant<size_t
}
write_response(id, "{}");
}
else if(method == "workspace/configuration") {
auto search_path = root_path;
auto file = '.' + language_id + "-lsp-configuration-response.json";
boost::filesystem::path settings_path;
while(true) {
boost::system::error_code ec;
auto path = search_path / file;
if(boost::filesystem::exists(path, ec)) {
settings_path = std::move(path);
break;
}
if(search_path == search_path.root_directory())
break;
search_path = search_path.parent_path();
}
if(!settings_path.empty())
write_response(id, filesystem::read(settings_path));
else
write_response(id, "{}");
}
else
write_response(id, "{}"); // TODO: write error instead on unsupported methods
}
Source::LanguageProtocolView::LanguageProtocolView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language, std::string language_id_, std::string language_server_)
: Source::BaseView(file_path, language), Source::View(file_path, language), uri(filesystem::get_uri_from_path(file_path)), uri_escaped(JSON::escape_string(uri)), language_id(std::move(language_id_)), language_server(std::move(language_server_)), client(LanguageProtocol::Client::get(file_path, language_id, language_server)) {
: Source::BaseView(file_path, language), Source::GenericView(file_path, language, false), uri(filesystem::get_uri_from_path(file_path)), uri_escaped(JSON::escape_string(uri)), language_id(std::move(language_id_)), language_server(std::move(language_server_)), client(LanguageProtocol::Client::get(file_path, language_id, language_server)) {
initialize();
}
@ -1059,9 +1093,7 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() {
std::unordered_map<std::string, std::vector<std::string>> file_lines;
std::vector<std::pair<Offset, std::string>> usages;
auto c = static_cast<size_t>(-1);
for(auto &location : locations) {
++c;
usages.emplace_back(Offset(location.range.start.line, location.range.start.character, location.file), std::string());
auto &usage = usages.back();
Source::View *view = nullptr;
@ -1345,8 +1377,19 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() {
std::promise<void> result_processed;
Gtk::TextIter start, end;
get_buffer()->get_selection_bounds(start, end);
bool first = true;
std::stringstream ss;
for(auto &diagnostic : last_diagnostics) {
if(get_iter_at_line_pos(diagnostic->range.start.line, diagnostic->range.start.character) <= start &&
get_iter_at_line_pos(diagnostic->range.end.line, diagnostic->range.end.character) >= end) {
if(!first)
ss << ',';
ss << *diagnostic->object;
first = false;
}
}
std::vector<std::pair<std::string, std::shared_ptr<JSON>>> results;
write_request("textDocument/codeAction", to_string({make_range({start.get_line(), get_line_pos(start)}, {end.get_line(), get_line_pos(end)}), {"context", "{\"diagnostics\":[]}"}}), [&result_processed, &results](JSON &&result, bool error) {
write_request("textDocument/codeAction", to_string({make_range({start.get_line(), get_line_pos(start)}, {end.get_line(), get_line_pos(end)}), {"context", "{\"diagnostics\":[" + ss.str() + "]}"}}), [&result_processed, &results](JSON &&result, bool error) {
if(!error) {
for(auto &code_action : result.array_or_empty()) {
auto title = code_action.string_or("title", "");
@ -1491,10 +1534,12 @@ void Source::LanguageProtocolView::setup_signals() {
}
void Source::LanguageProtocolView::setup_autocomplete() {
autocomplete = std::make_unique<Autocomplete>(this, interactive_completion, last_keyval, false, true);
if(!capabilities.completion)
if(!capabilities.completion) {
GenericView::setup_autocomplete();
return;
}
autocomplete = std::make_unique<Autocomplete>(this, interactive_completion, last_keyval, false, true);
non_interactive_completion = [this] {
if(CompletionDialog::get() && CompletionDialog::get()->is_visible())
@ -1975,7 +2020,7 @@ void Source::LanguageProtocolView::setup_autocomplete() {
};
}
void Source::LanguageProtocolView::update_diagnostics_async(std::vector<LanguageProtocol::Diagnostic> &&diagnostics) {
void Source::LanguageProtocolView::update_diagnostics_async(std::vector<std::shared_ptr<LanguageProtocol::Diagnostic>> &&diagnostics) {
size_t last_count = ++update_diagnostics_async_count;
if(capabilities.code_action && !diagnostics.empty()) {
dispatcher.post([this, diagnostics = std::move(diagnostics), last_count]() mutable {
@ -1986,12 +2031,12 @@ void Source::LanguageProtocolView::update_diagnostics_async(std::vector<Language
for(auto &diagnostic : diagnostics) {
if(!first)
ss << ',';
ss << *diagnostic.object;
ss << *diagnostic->object;
first = false;
}
std::pair<std::string, std::string> range;
if(diagnostics.size() == 1) // Use diagnostic range if only one diagnostic, otherwise use whole buffer
range = make_range({diagnostics[0].range.start.line, diagnostics[0].range.start.character}, {diagnostics[0].range.end.line, diagnostics[0].range.end.character});
range = make_range({diagnostics[0]->range.start.line, diagnostics[0]->range.start.character}, {diagnostics[0]->range.end.line, diagnostics[0]->range.end.character});
else {
auto start = get_buffer()->begin();
auto end = get_buffer()->end();
@ -2027,8 +2072,8 @@ void Source::LanguageProtocolView::update_diagnostics_async(std::vector<Language
if(!quickfix_diagnostics.empty()) {
for(auto &diagnostic : diagnostics) {
for(auto &quickfix_diagnostic : quickfix_diagnostics) {
if(diagnostic.message == quickfix_diagnostic.message && diagnostic.range == quickfix_diagnostic.range) {
auto pair = diagnostic.quickfixes.emplace(title, std::set<Source::FixIt>{});
if(diagnostic->message == quickfix_diagnostic.message && diagnostic->range == quickfix_diagnostic.range) {
auto pair = diagnostic->quickfixes.emplace(title, std::set<Source::FixIt>{});
pair.first->second.emplace(
text_edit.new_text,
edit->file,
@ -2041,8 +2086,8 @@ void Source::LanguageProtocolView::update_diagnostics_async(std::vector<Language
}
else { // Workaround for language server that does not report quickfix diagnostics
for(auto &diagnostic : diagnostics) {
if(text_edit.range.start.line == diagnostic.range.start.line) {
auto pair = diagnostic.quickfixes.emplace(title, std::set<Source::FixIt>{});
if(text_edit.range.start.line == diagnostic->range.start.line) {
auto pair = diagnostic->quickfixes.emplace(title, std::set<Source::FixIt>{});
pair.first->second.emplace(
text_edit.new_text,
edit->file,
@ -2067,9 +2112,8 @@ void Source::LanguageProtocolView::update_diagnostics_async(std::vector<Language
result_processed.get_future().get();
dispatcher.post([this, diagnostics = std::move(diagnostics), last_count]() mutable {
if(last_count == update_diagnostics_async_count) {
if(capabilities.type_coverage)
last_diagnostics = diagnostics;
update_diagnostics(std::move(diagnostics));
update_diagnostics(diagnostics);
last_diagnostics = std::move(diagnostics);
}
});
});
@ -2078,15 +2122,14 @@ void Source::LanguageProtocolView::update_diagnostics_async(std::vector<Language
else {
dispatcher.post([this, diagnostics = std::move(diagnostics), last_count]() mutable {
if(last_count == update_diagnostics_async_count) {
if(capabilities.type_coverage)
last_diagnostics = diagnostics;
update_diagnostics(std::move(diagnostics));
update_diagnostics(diagnostics);
last_diagnostics = std::move(diagnostics);
}
});
}
}
void Source::LanguageProtocolView::update_diagnostics(std::vector<LanguageProtocol::Diagnostic> diagnostics) {
void Source::LanguageProtocolView::update_diagnostics(const std::vector<std::shared_ptr<LanguageProtocol::Diagnostic>> &diagnostics) {
diagnostic_offsets.clear();
diagnostic_tooltips.clear();
fix_its.clear();
@ -2097,8 +2140,8 @@ void Source::LanguageProtocolView::update_diagnostics(std::vector<LanguageProtoc
num_fix_its = 0;
for(auto &diagnostic : diagnostics) {
auto start = get_iter_at_line_pos(diagnostic.range.start.line, diagnostic.range.start.character);
auto end = get_iter_at_line_pos(diagnostic.range.end.line, diagnostic.range.end.character);
auto start = get_iter_at_line_pos(diagnostic->range.start.line, diagnostic->range.start.character);
auto end = get_iter_at_line_pos(diagnostic->range.end.line, diagnostic->range.end.character);
if(start == end) {
if(!end.ends_line())
@ -2109,47 +2152,47 @@ void Source::LanguageProtocolView::update_diagnostics(std::vector<LanguageProtoc
}
bool error = false;
if(diagnostic.severity >= 2)
if(diagnostic->severity >= 2)
num_warnings++;
else {
num_errors++;
error = true;
}
num_fix_its += diagnostic.quickfixes.size();
num_fix_its += diagnostic->quickfixes.size();
for(auto &quickfix : diagnostic.quickfixes)
for(auto &quickfix : diagnostic->quickfixes)
fix_its.insert(fix_its.end(), quickfix.second.begin(), quickfix.second.end());
add_diagnostic_tooltip(start, end, error, [this, diagnostic = std::move(diagnostic)](Tooltip &tooltip) {
add_diagnostic_tooltip(start, end, error, [this, diagnostic](Tooltip &tooltip) {
if(language_id == "python" && !client->pyright) { // pylsp might support markdown in the future
tooltip.insert_with_links_tagged(diagnostic.message);
tooltip.insert_with_links_tagged(diagnostic->message);
return;
}
tooltip.insert_markdown(diagnostic.message);
tooltip.insert_markdown(diagnostic->message);
if(!diagnostic.related_informations.empty()) {
if(!diagnostic->related_informations.empty()) {
auto link_tag = tooltip.buffer->get_tag_table()->lookup("link");
for(size_t i = 0; i < diagnostic.related_informations.size(); ++i) {
auto link = filesystem::get_relative_path(diagnostic.related_informations[i].location.file, file_path.parent_path()).string();
link += ':' + std::to_string(diagnostic.related_informations[i].location.range.start.line + 1);
link += ':' + std::to_string(diagnostic.related_informations[i].location.range.start.character + 1);
for(size_t i = 0; i < diagnostic->related_informations.size(); ++i) {
auto link = filesystem::get_relative_path(diagnostic->related_informations[i].location.file, file_path.parent_path()).string();
link += ':' + std::to_string(diagnostic->related_informations[i].location.range.start.line + 1);
link += ':' + std::to_string(diagnostic->related_informations[i].location.range.start.character + 1);
if(i == 0)
tooltip.buffer->insert_at_cursor("\n\n");
else
tooltip.buffer->insert_at_cursor("\n");
tooltip.insert_markdown(diagnostic.related_informations[i].message);
tooltip.insert_markdown(diagnostic->related_informations[i].message);
tooltip.buffer->insert_at_cursor(": ");
tooltip.buffer->insert_with_tag(tooltip.buffer->get_insert()->get_iter(), link, link_tag);
}
}
if(!diagnostic.quickfixes.empty()) {
if(diagnostic.quickfixes.size() == 1)
if(!diagnostic->quickfixes.empty()) {
if(diagnostic->quickfixes.size() == 1)
tooltip.buffer->insert_at_cursor("\n\nFix-it:");
else
tooltip.buffer->insert_at_cursor("\n\nFix-its:");
for(auto &quickfix : diagnostic.quickfixes) {
for(auto &quickfix : diagnostic->quickfixes) {
tooltip.buffer->insert_at_cursor("\n");
tooltip.insert_markdown(quickfix.first);
}

13
src/source_language_protocol.hpp

@ -3,7 +3,7 @@
#include "json.hpp"
#include "mutex.hpp"
#include "process.hpp"
#include "source.hpp"
#include "source_generic.hpp"
#include <atomic>
#include <boost/optional.hpp>
#include <list>
@ -83,7 +83,7 @@ namespace LanguageProtocol {
std::vector<RelatedInformation> related_informations;
std::map<std::string, std::set<Source::FixIt>> quickfixes;
/// Diagnostic object for textDocument/codeAction on new diagnostics
std::shared_ptr<JSON> object;
std::unique_ptr<JSON> object;
};
class TextEdit {
@ -200,7 +200,7 @@ namespace LanguageProtocol {
} // namespace LanguageProtocol
namespace Source {
class LanguageProtocolView : public View {
class LanguageProtocolView : public GenericView {
friend class LanguageProtocol::Client;
public:
@ -231,10 +231,10 @@ namespace Source {
void write_did_change_notification(const std::vector<std::pair<std::string, std::string>> &params);
std::atomic<size_t> update_diagnostics_async_count = {0};
void update_diagnostics(std::vector<LanguageProtocol::Diagnostic> diagnostics);
void update_diagnostics(const std::vector<std::shared_ptr<LanguageProtocol::Diagnostic>> &diagnostics);
public:
void update_diagnostics_async(std::vector<LanguageProtocol::Diagnostic> &&diagnostics);
void update_diagnostics_async(std::vector<std::shared_ptr<LanguageProtocol::Diagnostic>> &&diagnostics);
Gtk::TextIter get_iter_at_line_pos(int line, int pos) override;
@ -298,8 +298,7 @@ namespace Source {
/// Used when calling completionItem/resolve. Also increased in autocomplete->on_hide.
std::atomic<size_t> set_tooltip_count = {0};
/// Used by update_type_coverage only (capabilities.type_coverage == true)
std::vector<LanguageProtocol::Diagnostic> last_diagnostics;
std::vector<std::shared_ptr<LanguageProtocol::Diagnostic>> last_diagnostics;
sigc::connection update_type_coverage_connection;
std::list<std::pair<Mark, Mark>> type_coverage_marks;

Loading…
Cancel
Save