From e5d469e3f1dbd9daee3c122fff0886cba0cccf7e Mon Sep 17 00:00:00 2001 From: eidheim Date: Wed, 25 Jul 2018 22:40:12 +0200 Subject: [PATCH] Language protocol: Cleanup, and added support for relatedInformation. Also cleanup in the way tooltip buffers are set. Finally, improved rename handling. --- src/autocomplete.cc | 12 +- src/ctags.cc | 9 +- src/filesystem.cc | 56 ++-- src/filesystem.h | 4 +- src/project.cc | 97 ++---- src/source.cc | 23 +- src/source.h | 4 +- src/source_base.cc | 6 + src/source_clang.cc | 34 ++- src/source_clang.h | 1 + src/source_diff.cc | 4 +- src/source_language_protocol.cc | 522 ++++++++++++++------------------ src/source_language_protocol.h | 64 +++- src/terminal.cc | 14 +- src/tooltips.cc | 74 ++++- src/tooltips.h | 8 +- src/window.cc | 2 - tests/filesystem_test.cc | 5 + tests/stubs/tooltips.cc | 7 +- 19 files changed, 503 insertions(+), 443 deletions(-) diff --git a/src/autocomplete.cc b/src/autocomplete.cc index fb2e9ad..8e75f66 100644 --- a/src/autocomplete.cc +++ b/src/autocomplete.cc @@ -155,16 +155,10 @@ void Autocomplete::setup_dialog() { tooltips.hide(); else { tooltips.clear(); - auto create_tooltip_buffer = [this, tooltip = std::move(tooltip)]() { - auto tooltip_buffer = Gtk::TextBuffer::create(view->get_buffer()->get_tag_table()); - - tooltip_buffer->insert(tooltip_buffer->get_insert()->get_iter(), tooltip); - - return tooltip_buffer; - }; - auto iter = CompletionDialog::get()->start_mark->get_iter(); - tooltips.emplace_back(create_tooltip_buffer, view, view->get_buffer()->create_mark(iter), view->get_buffer()->create_mark(iter)); + tooltips.emplace_back(view, view->get_buffer()->create_mark(iter), view->get_buffer()->create_mark(iter), [tooltip = std::move(tooltip)](const Glib::RefPtr &buffer) { + buffer->insert(buffer->get_insert()->get_iter(), tooltip); + }); tooltips.show(true); } diff --git a/src/ctags.cc b/src/ctags.cc index 9fda5fd..5b5b0d5 100644 --- a/src/ctags.cc +++ b/src/ctags.cc @@ -13,13 +13,8 @@ std::pair> Ctags::ge auto run_path = build->project_path; std::string exclude; if(!run_path.empty()) { - auto relative_default_path = filesystem::get_relative_path(build->get_default_path(), run_path); - if(!relative_default_path.empty()) - exclude += " --exclude=" + relative_default_path.string(); - - auto relative_debug_path = filesystem::get_relative_path(build->get_debug_path(), run_path); - if(!relative_debug_path.empty()) - exclude += " --exclude=" + relative_debug_path.string(); + exclude += " --exclude=" + filesystem::get_relative_path(build->get_default_path(), run_path).string(); + exclude += " --exclude=" + filesystem::get_relative_path(build->get_debug_path(), run_path).string(); } else { boost::system::error_code ec; diff --git a/src/filesystem.cc b/src/filesystem.cc index cbca581..fdebe10 100644 --- a/src/filesystem.cc +++ b/src/filesystem.cc @@ -91,13 +91,21 @@ std::string filesystem::unescape_argument(const std::string &argument) { } boost::filesystem::path filesystem::get_home_path() noexcept { + static boost::filesystem::path home_path; + if(!home_path.empty()) + return home_path; std::vector environment_variables = {"HOME", "AppData"}; char *ptr = nullptr; for(auto &variable : environment_variables) { ptr = std::getenv(variable.c_str()); - boost::system::error_code ec; - if(ptr != nullptr && boost::filesystem::exists(ptr, ec)) - return ptr; + if(ptr != nullptr) { + boost::system::error_code ec; + boost::filesystem::path path(ptr); + if(boost::filesystem::exists(path, ec)) { + home_path = std::move(path); + return home_path; + } + } } return boost::filesystem::path(); } @@ -106,11 +114,26 @@ boost::filesystem::path filesystem::get_short_path(const boost::filesystem::path #ifdef _WIN32 return path; #else - static auto home_path = get_home_path(); - if(!home_path.empty()) { - auto relative_path = filesystem::get_relative_path(path, home_path); - if(!relative_path.empty()) - return "~" / relative_path; + auto home_path = get_home_path(); + if(!home_path.empty() && file_in_path(path, home_path)) + return "~" / get_relative_path(path, home_path); + return path; +#endif +} + +boost::filesystem::path filesystem::get_long_path(const boost::filesystem::path &path) noexcept { +#ifdef _WIN32 + return path; +#else + if(!path.empty() && *path.begin() == "~") { + auto long_path = get_home_path(); + if(!long_path.empty()) { + auto it = path.begin(); + ++it; + for(; it != path.end(); ++it) + long_path /= *it; + return long_path; + } } return path; #endif @@ -158,21 +181,20 @@ boost::filesystem::path filesystem::get_normal_path(const boost::filesystem::pat boost::filesystem::path filesystem::get_relative_path(const boost::filesystem::path &path, const boost::filesystem::path &base) noexcept { boost::filesystem::path relative_path; - - if(std::distance(path.begin(), path.end()) < std::distance(base.begin(), base.end())) - return boost::filesystem::path(); - auto base_it = base.begin(); auto path_it = path.begin(); - while(path_it != path.end() && base_it != base.end()) { - if(*path_it != *base_it) - return boost::filesystem::path(); + while(path_it != path.end() && base_it != base.end() && *path_it == *base_it) { ++path_it; ++base_it; } - for(; path_it != path.end(); ++path_it) + while(base_it != base.end()) { + relative_path /= ".."; + ++base_it; + } + while(path_it != path.end()) { relative_path /= *path_it; - + ++path_it; + } return relative_path; } diff --git a/src/filesystem.h b/src/filesystem.h index 0a2370c..222475f 100644 --- a/src/filesystem.h +++ b/src/filesystem.h @@ -20,7 +20,10 @@ public: static std::string unescape_argument(const std::string &argument); static boost::filesystem::path get_home_path() noexcept; + /// Replaces home path with ~ static boost::filesystem::path get_short_path(const boost::filesystem::path &path) noexcept; + /// Replaces ~ with home path (boost::filesystem does not recognize ~) + static boost::filesystem::path get_long_path(const boost::filesystem::path &path) noexcept; static bool file_in_path(const boost::filesystem::path &file_path, const boost::filesystem::path &path); static boost::filesystem::path find_file_in_path_parents(const std::string &file_name, const boost::filesystem::path &path); @@ -28,7 +31,6 @@ public: /// Return path with dot, dot-dot and directory separator elements removed static boost::filesystem::path get_normal_path(const boost::filesystem::path &path) noexcept; - /// Returns empty path on failure static boost::filesystem::path get_relative_path(const boost::filesystem::path &path, const boost::filesystem::path &base) noexcept; /// Return executable with latest version in filename on systems that is lacking executable_name symbolic link diff --git a/src/project.cc b/src/project.cc index b66f802..a457420 100644 --- a/src/project.cc +++ b/src/project.cc @@ -602,9 +602,9 @@ void Project::LLDB::debug_show_variables() { return; } self->debug_variable_tooltips.clear(); - auto create_tooltip_buffer = [rows, view, index]() { + + auto set_tooltip_buffer = [rows, index](const Glib::RefPtr &buffer) { auto variable = (*rows)[index]; - auto tooltip_buffer = view ? Gtk::TextBuffer::create(view->get_buffer()->get_tag_table()) : Gtk::TextBuffer::create(); Glib::ustring value = variable.value; if(!value.empty()) { @@ -614,19 +614,13 @@ void Project::LLDB::debug_show_variables() { next_char_iter++; value.replace(iter, next_char_iter, "?"); } - if(view) - tooltip_buffer->insert(tooltip_buffer->get_insert()->get_iter(), value.substr(0, value.size() - 1)); - else - tooltip_buffer->insert(tooltip_buffer->get_insert()->get_iter(), value.substr(0, value.size() - 1)); + buffer->insert(buffer->get_insert()->get_iter(), value.substr(0, value.size() - 1)); } - - return tooltip_buffer; }; - if(view) - self->debug_variable_tooltips.emplace_back(create_tooltip_buffer, view, view->get_buffer()->create_mark(iter), view->get_buffer()->create_mark(iter)); + self->debug_variable_tooltips.emplace_back(view, view->get_buffer()->create_mark(iter), view->get_buffer()->create_mark(iter), std::move(set_tooltip_buffer)); else - self->debug_variable_tooltips.emplace_back(create_tooltip_buffer); + self->debug_variable_tooltips.emplace_back(std::move(set_tooltip_buffer)); self->debug_variable_tooltips.show(true); }; @@ -707,97 +701,68 @@ void Project::LanguageProtocol::show_symbols() { SelectionDialog::get()->on_search_entry_changed = nullptr; // To delete client object }; - auto offsets = std::make_shared>(); + auto locations = std::make_shared>(); if(capabilities.workspace_symbol) { - SelectionDialog::get()->on_search_entry_changed = [client, project_path, offsets](const std::string &text) { + SelectionDialog::get()->on_search_entry_changed = [client, project_path, locations](const std::string &text) { if(text.size() > 1) return; else { - offsets->clear(); + locations->clear(); SelectionDialog::get()->erase_rows(); if(text.empty()) return; } std::vector names; std::promise result_processed; - client->write_request(nullptr, "workspace/symbol", R"("query":")" + text + '"', [&result_processed, &names, offsets, project_path](const boost::property_tree::ptree &result, bool error) { + client->write_request(nullptr, "workspace/symbol", R"("query":")" + text + '"', [&result_processed, &names, locations, project_path](const boost::property_tree::ptree &result, bool error) { if(!error) { for(auto it = result.begin(); it != result.end(); ++it) { - auto name = it->second.get("name", ""); - if(!name.empty()) { - auto location_it = it->second.find("location"); - if(location_it != it->second.not_found()) { - auto file = location_it->second.get("uri", ""); - if(file.size() > 7) { - file.erase(0, 7); - auto range_it = location_it->second.find("range"); - if(range_it != location_it->second.not_found()) { - auto start_it = range_it->second.find("start"); - if(start_it != range_it->second.not_found()) { - try { - offsets->emplace_back(Source::Offset(start_it->second.get("line"), start_it->second.get("character"), file)); - names.emplace_back(name); - } - catch(...) { - } - } - } - } + try { + ::LanguageProtocol::Location location(it->second.get_child("location")); + if(filesystem::file_in_path(location.uri, *project_path)) { + locations->emplace_back(std::move(location)); + names.emplace_back(it->second.get("name")); } } + catch(...) { + } } } result_processed.set_value(); }); result_processed.get_future().get(); - for(size_t c = 0; c < offsets->size() && c < names.size(); ++c) - SelectionDialog::get()->add_row(filesystem::get_relative_path((*offsets)[c].file_path, *project_path).string() + ':' + std::to_string((*offsets)[c].line + 1) + ':' + std::to_string((*offsets)[c].index + 1) + ": " + names[c]); + for(size_t c = 0; c < locations->size() && c < names.size(); ++c) + SelectionDialog::get()->add_row(filesystem::get_relative_path((*locations)[c].uri, *project_path).string() + ':' + std::to_string((*locations)[c].range.start.line + 1) + ':' + std::to_string((*locations)[c].range.start.character + 1) + ": " + names[c]); }; } else { std::vector names; std::promise result_processed; - client->write_request(nullptr, "textDocument/documentSymbol", R"("textDocument":{"uri":")" + language_protocol_view->uri + "\"}", [&result_processed, &names, offsets, project_path](const boost::property_tree::ptree &result, bool error) { + client->write_request(nullptr, "textDocument/documentSymbol", R"("textDocument":{"uri":"file://)" + language_protocol_view->file_path.string() + "\"}", [&result_processed, &names, locations, project_path](const boost::property_tree::ptree &result, bool error) { if(!error) { for(auto it = result.begin(); it != result.end(); ++it) { - auto name = it->second.get("name", ""); - if(!name.empty()) { - auto location_it = it->second.find("location"); - if(location_it != it->second.not_found()) { - auto file = location_it->second.get("uri", ""); - if(file.size() > 7) { - file.erase(0, 7); - auto range_it = location_it->second.find("range"); - if(range_it != location_it->second.not_found()) { - auto start_it = range_it->second.find("start"); - if(start_it != range_it->second.not_found()) { - try { - offsets->emplace_back(Source::Offset(start_it->second.get("line"), start_it->second.get("character"), file)); - names.emplace_back(name); - } - catch(...) { - } - } - } - } - } + try { + locations->emplace_back(it->second.get_child("location")); + names.emplace_back(it->second.get("name")); + } + catch(...) { } } } result_processed.set_value(); }); result_processed.get_future().get(); - for(size_t c = 0; c < offsets->size() && c < names.size(); ++c) - SelectionDialog::get()->add_row(std::to_string((*offsets)[c].line + 1) + ':' + std::to_string((*offsets)[c].index + 1) + ": " + names[c]); + for(size_t c = 0; c < locations->size() && c < names.size(); ++c) + SelectionDialog::get()->add_row(std::to_string((*locations)[c].range.start.line + 1) + ':' + std::to_string((*locations)[c].range.start.character + 1) + ": " + names[c]); } - SelectionDialog::get()->on_select = [offsets](unsigned int index, const std::string &text, bool hide_window) { - auto &offset = (*offsets)[index]; - if(!boost::filesystem::is_regular_file(offset.file_path)) + SelectionDialog::get()->on_select = [locations](unsigned int index, const std::string &text, bool hide_window) { + auto &offset = (*locations)[index]; + if(!boost::filesystem::is_regular_file(offset.uri)) return; - Notebook::get().open(offset.file_path); + Notebook::get().open(offset.uri); auto view = Notebook::get().get_current_view(); - view->place_cursor_at_line_offset(offset.line, offset.index); + view->place_cursor_at_line_offset(offset.range.start.line, offset.range.start.character); view->scroll_to_cursor_delayed(view, true, false); }; diff --git a/src/source.cc b/src/source.cc index 0d50beb..66ef3df 100644 --- a/src/source.cc +++ b/src/source.cc @@ -161,6 +161,8 @@ Source::View::View(const boost::filesystem::path &file_path, const Glib::RefPtr< mark_attr_debug_breakpoint_and_stop->set_background(rgba); set_mark_attributes("debug_breakpoint_and_stop", mark_attr_debug_breakpoint_and_stop, 102); + link_tag = get_buffer()->create_tag("link"); + get_buffer()->signal_changed().connect([this]() { if(update_status_location) update_status_location(this); @@ -507,6 +509,9 @@ void Source::View::configure() { } //TODO: clear tag_class and param_spec? + link_tag->property_foreground_rgba() = get_style_context()->get_color(Gtk::StateFlags::STATE_FLAG_LINK); + link_tag->property_underline() = Pango::Underline::UNDERLINE_SINGLE; + if(Config::get().menu.keys["source_show_completion"].empty()) { get_completion()->unblock_interactive(); interactive_completion = true; @@ -684,7 +689,9 @@ void Source::View::setup_format_style(bool is_generic_view) { if(start == end) start.forward_char(); - add_diagnostic_tooltip(start, end, sm[1].str(), true); + add_diagnostic_tooltip(start, end, true, [sm = std::move(sm)](const Glib::RefPtr &buffer) { + buffer->insert_at_cursor(sm[1].str()); + }); } catch(...) { } @@ -1198,18 +1205,16 @@ void Source::View::hide_tooltips() { diagnostic_tooltips.hide(); } -void Source::View::add_diagnostic_tooltip(const Gtk::TextIter &start, const Gtk::TextIter &end, std::string spelling, bool error) { +void Source::View::add_diagnostic_tooltip(const Gtk::TextIter &start, const Gtk::TextIter &end, bool error, std::function &)> &&set_buffer) { diagnostic_offsets.emplace(start.get_offset()); std::string severity_tag_name = error ? "def:error" : "def:warning"; - auto create_tooltip_buffer = [this, spelling = std::move(spelling), error, severity_tag_name]() { - auto tooltip_buffer = Gtk::TextBuffer::create(get_buffer()->get_tag_table()); - tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), error ? "Error" : "Warning", severity_tag_name); - tooltip_buffer->insert(tooltip_buffer->get_insert()->get_iter(), ":\n" + spelling); - return tooltip_buffer; - }; - diagnostic_tooltips.emplace_back(create_tooltip_buffer, this, get_buffer()->create_mark(start), get_buffer()->create_mark(end)); + diagnostic_tooltips.emplace_back(this, get_buffer()->create_mark(start), get_buffer()->create_mark(end), [error, severity_tag_name, set_buffer = std::move(set_buffer)](const Glib::RefPtr &buffer) { + buffer->insert_with_tag(buffer->get_insert()->get_iter(), error ? "Error" : "Warning", severity_tag_name); + buffer->insert(buffer->get_insert()->get_iter(), ":\n"); + set_buffer(buffer); + }); get_buffer()->apply_tag_by_name(severity_tag_name + "_underline", start, end); diff --git a/src/source.h b/src/source.h index ea42d0d..1b057bf 100644 --- a/src/source.h +++ b/src/source.h @@ -108,7 +108,7 @@ namespace Source { Glib::RefPtr similar_symbol_tag; sigc::connection delayed_tag_similar_symbols_connection; virtual void show_diagnostic_tooltips(const Gdk::Rectangle &rectangle) { diagnostic_tooltips.show(rectangle); } - void add_diagnostic_tooltip(const Gtk::TextIter &start, const Gtk::TextIter &end, std::string spelling, bool error); + void add_diagnostic_tooltip(const Gtk::TextIter &start, const Gtk::TextIter &end, bool error, std::function &)> &&set_buffer); void clear_diagnostic_tooltips(); virtual void show_type_tooltips(const Gdk::Rectangle &rectangle) {} gdouble on_motion_last_x = 0.0; @@ -169,6 +169,8 @@ namespace Source { int multiple_cursors_erase_backward_length; int multiple_cursors_erase_forward_length; bool on_key_press_event_multiple_cursors(GdkEventKey *key); + + Glib::RefPtr link_tag; /// Used in tooltips }; class GenericView : public View { diff --git a/src/source_base.cc b/src/source_base.cc index 7260426..6c6e460 100644 --- a/src/source_base.cc +++ b/src/source_base.cc @@ -181,6 +181,12 @@ void Source::BaseView::replace_text(const std::string &new_text) { void Source::BaseView::rename(const boost::filesystem::path &path) { file_path = path; + boost::system::error_code ec; + last_write_time = boost::filesystem::last_write_time(file_path, ec); + if(ec) + last_write_time = static_cast(-1); + monitor_file(); + if(update_status_file_path) update_status_file_path(this); if(update_tab_label) diff --git a/src/source_clang.cc b/src/source_clang.cc index e9dee64..a32e993 100644 --- a/src/source_clang.cc +++ b/src/source_clang.cc @@ -11,6 +11,7 @@ #include "documentation_cppreference.h" #include "filesystem.h" #include "info.h" +#include "notebook.h" #include "selection_dialog.h" #include "usages_clang.h" @@ -43,6 +44,14 @@ Source::ClangViewParse::ClangViewParse(const boost::filesystem::path &file_path, }); } +void Source::ClangViewParse::rename(const boost::filesystem::path &path) { + Source::DiffView::rename(path); + if(Notebook::get().get_current_view() == this) + full_reparse(); + else + full_reparse_needed = true; +} + bool Source::ClangViewParse::save() { if(!Source::View::save()) return false; @@ -338,7 +347,9 @@ void Source::ClangViewParse::update_diagnostics() { if(!fix_its_string.empty()) diagnostic.spelling += "\n\n" + fix_its_string; - add_diagnostic_tooltip(start, end, diagnostic.spelling, error); + add_diagnostic_tooltip(start, end, error, [spelling = std::move(diagnostic.spelling)](const Glib::RefPtr &buffer) { + buffer->insert_at_cursor(spelling); + }); } } status_diagnostics = std::make_tuple(num_warnings, num_errors, num_fix_its); @@ -371,12 +382,12 @@ void Source::ClangViewParse::show_type_tooltips(const Gdk::Rectangle &rectangle) if(referenced) { auto start = get_buffer()->get_iter_at_line_index(token_offsets.first.line - 1, token_offsets.first.index - 1); auto end = get_buffer()->get_iter_at_line_index(token_offsets.second.line - 1, token_offsets.second.index - 1); - auto create_tooltip_buffer = [this, &token, &start, &end]() { - auto tooltip_buffer = Gtk::TextBuffer::create(get_buffer()->get_tag_table()); - tooltip_buffer->insert(tooltip_buffer->get_insert()->get_iter(), "Type: " + token.get_cursor().get_type_description()); + + type_tooltips.emplace_back(this, get_buffer()->create_mark(start), get_buffer()->create_mark(end), [this, token, start, end](const Glib::RefPtr &buffer) { + buffer->insert(buffer->get_insert()->get_iter(), "Type: " + token.get_cursor().get_type_description()); auto brief_comment = token.get_cursor().get_brief_comments(); if(brief_comment != "") - tooltip_buffer->insert(tooltip_buffer->get_insert()->get_iter(), "\n\n" + brief_comment); + buffer->insert(buffer->get_insert()->get_iter(), "\n\n" + brief_comment); #ifdef JUCI_ENABLE_DEBUG if(Debug::LLDB::get().is_stopped()) { @@ -385,8 +396,9 @@ void Source::ClangViewParse::show_type_tooltips(const Gdk::Rectangle &rectangle) Glib::ustring value_type = "Value"; auto iter = start; + auto corrected_start = start; while((*iter >= 'a' && *iter <= 'z') || (*iter >= 'A' && *iter <= 'Z') || (*iter >= '0' && *iter <= '9') || *iter == '_' || *iter == '.') { - start = iter; + corrected_start = iter; if(!iter.backward_char()) break; if(*iter == '>') { @@ -398,7 +410,7 @@ void Source::ClangViewParse::show_type_tooltips(const Gdk::Rectangle &rectangle) break; } } - auto spelling = get_buffer()->get_text(start, end).raw(); + auto spelling = get_buffer()->get_text(corrected_start, end).raw(); Glib::ustring debug_value; auto cursor_kind = referenced.get_kind(); @@ -421,16 +433,12 @@ void Source::ClangViewParse::show_type_tooltips(const Gdk::Rectangle &rectangle) next_char_iter++; debug_value.replace(iter, next_char_iter, "?"); } - tooltip_buffer->insert(tooltip_buffer->get_insert()->get_iter(), "\n\n" + value_type + ": " + debug_value.substr(pos + 3, debug_value.size() - (pos + 3) - 1)); + buffer->insert(buffer->get_insert()->get_iter(), "\n\n" + value_type + ": " + debug_value.substr(pos + 3, debug_value.size() - (pos + 3) - 1)); } } } #endif - - return tooltip_buffer; - }; - - type_tooltips.emplace_back(create_tooltip_buffer, this, get_buffer()->create_mark(start), get_buffer()->create_mark(end)); + }); type_tooltips.show(); return; } diff --git a/src/source_clang.h b/src/source_clang.h index 2155f4c..8a8ea6d 100644 --- a/src/source_clang.h +++ b/src/source_clang.h @@ -19,6 +19,7 @@ namespace Source { public: ClangViewParse(const boost::filesystem::path &file_path, const Glib::RefPtr &language); + void rename(const boost::filesystem::path &path) override; bool save() override; void configure() override; diff --git a/src/source_diff.cc b/src/source_diff.cc index 643e814..d3c2594 100644 --- a/src/source_diff.cc +++ b/src/source_diff.cc @@ -313,9 +313,9 @@ std::unique_ptr Source::DiffView::get_diff() { boost::filesystem::path relative_path; { std::unique_lock lock(canonical_file_path_mutex); - relative_path = filesystem::get_relative_path(canonical_file_path, work_path); - if(relative_path.empty()) + if(!filesystem::file_in_path(canonical_file_path, work_path)) throw std::runtime_error("not a relative path"); + relative_path = filesystem::get_relative_path(canonical_file_path, work_path); } return std::make_unique(repository->get_diff(relative_path)); } diff --git a/src/source_language_protocol.cc b/src/source_language_protocol.cc index 5a955d9..bf6b5b8 100644 --- a/src/source_language_protocol.cc +++ b/src/source_language_protocol.cc @@ -12,6 +12,9 @@ #include #include #include +#include + +const std::string flow_coverage_message = "Not covered by Flow"; LanguageProtocol::Client::Client(std::string root_uri_, std::string language_id_) : root_uri(std::move(root_uri_)), language_id(std::move(language_id_)) { process = std::make_unique(language_id + "-language-server", root_uri, [this](const char *bytes, size_t n) { @@ -297,21 +300,15 @@ void LanguageProtocol::Client::handle_server_request(const std::string &method, if(!uri.empty()) { auto diagnostics_pt = params.get_child("diagnostics", boost::property_tree::ptree()); for(auto it = diagnostics_pt.begin(); it != diagnostics_pt.end(); ++it) { - auto range_it = it->second.find("range"); - if(range_it != it->second.not_found()) { - auto start_it = range_it->second.find("start"); - auto end_it = range_it->second.find("end"); - if(start_it != range_it->second.not_found() && start_it != range_it->second.not_found()) { - diagnostics.emplace_back(Diagnostic{it->second.get("message", ""), - std::make_pair(Source::Offset(start_it->second.get("line", 0), start_it->second.get("character", 0)), - Source::Offset(end_it->second.get("line", 0), end_it->second.get("character", 0))), - it->second.get("severity", 0), uri}); - } + try { + diagnostics.emplace_back(it->second); + } + catch(...) { } } std::unique_lock lock(views_mutex); for(auto view : views) { - if(view->uri == uri) { + if(uri.size() >= 7 && uri.compare(7, std::string::npos, view->file_path.string()) == 0) { view->update_diagnostics(std::move(diagnostics)); break; } @@ -321,15 +318,11 @@ void LanguageProtocol::Client::handle_server_request(const std::string &method, } Source::LanguageProtocolView::LanguageProtocolView(const boost::filesystem::path &file_path, const Glib::RefPtr &language, std::string language_id_) - : Source::BaseView(file_path, language), Source::View(file_path, language), uri("file://" + file_path.string()), language_id(std::move(language_id_)), client(LanguageProtocol::Client::get(file_path, language_id)), autocomplete(this, interactive_completion, last_keyval, false) { + : Source::BaseView(file_path, language), Source::View(file_path, language), language_id(std::move(language_id_)), client(LanguageProtocol::Client::get(file_path, language_id)), autocomplete(this, interactive_completion, last_keyval, false) { configure(); get_source_buffer()->set_language(language); get_source_buffer()->set_highlight_syntax(true); - status_state = "initializing..."; - if(update_status_state) - update_status_state(this); - if(language_id == "javascript") { boost::filesystem::path project_path; auto build = Project::Build::create(file_path); @@ -345,30 +338,7 @@ Source::LanguageProtocolView::LanguageProtocolView(const boost::filesystem::path } } - initialize_thread = std::thread([this] { - auto capabilities = client->initialize(this); - - if(!flow_coverage_executable.empty()) - add_flow_coverage_tooltips(true); - - dispatcher.post([this, capabilities] { - this->capabilities = capabilities; - - std::string text = get_buffer()->get_text(); - escape_text(text); - client->write_notification("textDocument/didOpen", R"("textDocument":{"uri":")" + uri + R"(","languageId":")" + language_id + R"(","version":)" + std::to_string(document_version++) + R"(,"text":")" + text + "\"}"); - - setup_autocomplete(); - setup_navigation_and_refactoring(); - Menu::get().toggle_menu_items(); - - if(status_state == "initializing...") { - status_state = ""; - if(update_status_state) - update_status_state(this); - } - }); - }); + initialize(true); get_buffer()->signal_changed().connect([this] { get_buffer()->remove_tag(similar_symbol_tag, get_buffer()->begin(), get_buffer()->end()); @@ -398,7 +368,7 @@ Source::LanguageProtocolView::LanguageProtocolView(const boost::filesystem::path escape_text(text); content_changes = R"({"text":")" + text + "\"}"; } - client->write_notification("textDocument/didChange", R"("textDocument":{"uri":")" + uri + R"(","version":)" + std::to_string(document_version++) + "},\"contentChanges\":[" + content_changes + "]"); + client->write_notification("textDocument/didChange", R"("textDocument":{"uri":"file://)" + this->file_path.string() + R"(","version":)" + std::to_string(document_version++) + "},\"contentChanges\":[" + content_changes + "]"); }, false); get_buffer()->signal_erase().connect([this](const Gtk::TextBuffer::iterator &start, const Gtk::TextBuffer::iterator &end) { @@ -412,11 +382,48 @@ Source::LanguageProtocolView::LanguageProtocolView(const boost::filesystem::path escape_text(text); content_changes = R"({"text":")" + text + "\"}"; } - client->write_notification("textDocument/didChange", R"("textDocument":{"uri":")" + uri + R"(","version":)" + std::to_string(document_version++) + "},\"contentChanges\":[" + content_changes + "]"); + client->write_notification("textDocument/didChange", R"("textDocument":{"uri":"file://)" + this->file_path.string() + R"(","version":)" + std::to_string(document_version++) + "},\"contentChanges\":[" + content_changes + "]"); }, false); } -Source::LanguageProtocolView::~LanguageProtocolView() { +void Source::LanguageProtocolView::initialize(bool setup) { + status_diagnostics = std::make_tuple(0, 0, 0); + if(update_status_diagnostics) + update_status_diagnostics(this); + + status_state = "initializing..."; + if(update_status_state) + update_status_state(this); + + initialize_thread = std::thread([this, setup] { + if(!flow_coverage_executable.empty()) + add_flow_coverage_tooltips(true); + + auto capabilities = client->initialize(this); + + dispatcher.post([this, capabilities, setup] { + this->capabilities = capabilities; + + std::string text = get_buffer()->get_text(); + escape_text(text); + client->write_notification("textDocument/didOpen", R"("textDocument":{"uri":"file://)" + file_path.string() + R"(","languageId":")" + language_id + R"(","version":)" + std::to_string(document_version++) + R"(,"text":")" + text + "\"}"); + + if(setup) { + setup_autocomplete(); + setup_navigation_and_refactoring(); + Menu::get().toggle_menu_items(); + } + + if(status_state == "initializing...") { + status_state = ""; + if(update_status_state) + update_status_state(this); + } + }); + }); +} + +void Source::LanguageProtocolView::close() { if(initialize_thread.joinable()) initialize_thread.join(); @@ -424,12 +431,22 @@ Source::LanguageProtocolView::~LanguageProtocolView() { if(autocomplete.thread.joinable()) autocomplete.thread.join(); - client->write_notification("textDocument/didClose", R"("textDocument":{"uri":")" + uri + "\"}"); + client->write_notification("textDocument/didClose", R"("textDocument":{"uri":"file://)" + file_path.string() + "\"}"); client->close(this); - client = nullptr; } +Source::LanguageProtocolView::~LanguageProtocolView() { + close(); +} + +void Source::LanguageProtocolView::rename(const boost::filesystem::path &path) { + close(); + Source::DiffView::rename(path); + client = LanguageProtocol::Client::get(file_path, language_id); + initialize(false); +} + bool Source::LanguageProtocolView::save() { if(!Source::View::save()) return false; @@ -445,7 +462,7 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() { format_style = [this](bool continue_without_style_file) { if(!continue_without_style_file) { bool has_style_file = false; - auto style_file_search_path = this->file_path.parent_path(); + auto style_file_search_path = file_path.parent_path(); auto style_file = '.' + language_id + "-format"; while(true) { @@ -462,12 +479,7 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() { return; } - class Replace { - public: - Offset start, end; - std::string text; - }; - std::vector replaces; + std::vector text_edits; std::promise result_processed; std::string method; @@ -477,30 +489,20 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() { method = "textDocument/rangeFormatting"; Gtk::TextIter start, end; get_buffer()->get_selection_bounds(start, end); - params = R"("textDocument":{"uri":")" + uri + R"("},"range":{"start":{"line":)" + std::to_string(start.get_line()) + ",\"character\":" + std::to_string(start.get_line_offset()) + R"(},"end":{"line":)" + std::to_string(end.get_line()) + ",\"character\":" + std::to_string(end.get_line_offset()) + "}},\"options\":{" + options + "}"; + params = R"("textDocument":{"uri":"file://)" + file_path.string() + R"("},"range":{"start":{"line":)" + std::to_string(start.get_line()) + ",\"character\":" + std::to_string(start.get_line_offset()) + R"(},"end":{"line":)" + std::to_string(end.get_line()) + ",\"character\":" + std::to_string(end.get_line_offset()) + "}},\"options\":{" + options + "}"; } else { method = "textDocument/formatting"; - params = R"("textDocument":{"uri":")" + uri + R"("},"options":{)" + options + "}"; + params = R"("textDocument":{"uri":"file://)" + file_path.string() + R"("},"options":{)" + options + "}"; } - client->write_request(this, method, params, [&replaces, &result_processed](const boost::property_tree::ptree &result, bool error) { + client->write_request(this, method, params, [&text_edits, &result_processed](const boost::property_tree::ptree &result, bool error) { if(!error) { for(auto it = result.begin(); it != result.end(); ++it) { - auto range_it = it->second.find("range"); - auto text_it = it->second.find("newText"); - if(range_it != it->second.not_found() && text_it != it->second.not_found()) { - auto start_it = range_it->second.find("start"); - auto end_it = range_it->second.find("end"); - if(start_it != range_it->second.not_found() && end_it != range_it->second.not_found()) { - try { - replaces.emplace_back(Replace{Offset(start_it->second.get("line"), start_it->second.get("character")), - Offset(end_it->second.get("line"), end_it->second.get("character")), - text_it->second.get_value()}); - } - catch(...) { - } - } + try { + text_edits.emplace_back(it->second); + } + catch(...) { } } } @@ -510,20 +512,20 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() { auto end_iter = get_buffer()->end(); // If entire buffer is replaced: - if(replaces.size() == 1 && - replaces[0].start.line == 0 && replaces[0].start.index == 0 && - (replaces[0].end.line > static_cast(end_iter.get_line()) || - (replaces[0].end.line == static_cast(end_iter.get_line()) && replaces[0].end.index >= static_cast(end_iter.get_line_offset())))) { - replace_text(replaces[0].text); + if(text_edits.size() == 1 && + text_edits[0].range.start.line == 0 && text_edits[0].range.start.character == 0 && + (text_edits[0].range.end.line > static_cast(end_iter.get_line()) || + (text_edits[0].range.end.line == static_cast(end_iter.get_line()) && text_edits[0].range.end.character >= static_cast(end_iter.get_line_offset())))) { + replace_text(text_edits[0].new_text); } else { get_buffer()->begin_user_action(); - for(auto it = replaces.rbegin(); it != replaces.rend(); ++it) { - auto start = get_iter_at_line_pos(it->start.line, it->start.index); - auto end = get_iter_at_line_pos(it->end.line, it->end.index); + for(auto it = text_edits.rbegin(); it != text_edits.rend(); ++it) { + auto start = get_iter_at_line_pos(it->range.start.line, it->range.start.character); + auto end = get_iter_at_line_pos(it->range.end.line, it->range.end.character); get_buffer()->erase(start, end); - start = get_iter_at_line_pos(it->start.line, it->start.index); - get_buffer()->insert(start, it->text); + start = get_iter_at_line_pos(it->range.start.line, it->range.start.character); + get_buffer()->insert(start, it->new_text); } get_buffer()->end_user_action(); } @@ -549,44 +551,23 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() { if(capabilities.references || capabilities.document_highlight) { get_usages = [this] { auto iter = get_buffer()->get_insert()->get_iter(); - std::vector> usages; - std::vector end_offsets; + std::vector locations; std::promise result_processed; std::string method; - if(capabilities.document_highlight) - method = "textDocument/documentHighlight"; - else + if(capabilities.references) method = "textDocument/references"; + else + method = "textDocument/documentHighlight"; - client->write_request(this, method, R"("textDocument":{"uri":")" + uri + R"("}, "position": {"line": )" + std::to_string(iter.get_line()) + ", \"character\": " + std::to_string(iter.get_line_offset()) + R"(}, "context": {"includeDeclaration": true})", [this, &usages, &end_offsets, &result_processed](const boost::property_tree::ptree &result, bool error) { + client->write_request(this, method, R"("textDocument":{"uri":"file://)" + file_path.string() + R"("}, "position": {"line": )" + std::to_string(iter.get_line()) + ", \"character\": " + std::to_string(iter.get_line_offset()) + R"(}, "context": {"includeDeclaration": true})", [this, &locations, &result_processed](const boost::property_tree::ptree &result, bool error) { if(!error) { try { - for(auto it = result.begin(); it != result.end(); ++it) { - std::string path; - if(capabilities.document_highlight) - path = file_path.string(); - else { - path = it->second.get("uri", ""); - if(path.size() >= 7) - path.erase(0, 7); - else - continue; - } - auto range_it = it->second.find("range"); - if(range_it != it->second.not_found()) { - auto start_it = range_it->second.find("start"); - auto end_it = range_it->second.find("end"); - if(start_it != range_it->second.not_found() && end_it != range_it->second.not_found()) { - usages.emplace_back(std::make_pair(Offset(start_it->second.get("line"), start_it->second.get("character"), path), "")); - end_offsets.emplace_back(end_it->second.get("line"), end_it->second.get("character")); - } - } - } + for(auto it = result.begin(); it != result.end(); ++it) + locations.emplace_back(it->second, !capabilities.references ? file_path.string() : std::string()); } catch(...) { - usages.clear(); - end_offsets.clear(); + locations.clear(); } } result_processed.set_value(); @@ -624,30 +605,33 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() { line_ = line.raw(); }; - std::map> file_lines; + std::unordered_map> file_lines; + std::vector> usages; auto c = static_cast(-1); - for(auto &usage : usages) { + for(auto &location : locations) { ++c; + usages.emplace_back(Offset(location.range.start.line, location.range.start.character, location.uri), std::string()); + auto &usage = usages.back(); auto view_it = views.end(); for(auto it = views.begin(); it != views.end(); ++it) { - if(usage.first.file_path == (*it)->file_path) { + if(location.uri == (*it)->file_path) { view_it = it; break; } } if(view_it != views.end()) { - if(usage.first.line < static_cast((*view_it)->get_buffer()->get_line_count())) { - auto start = (*view_it)->get_buffer()->get_iter_at_line(usage.first.line); + if(location.range.start.line < static_cast((*view_it)->get_buffer()->get_line_count())) { + auto start = (*view_it)->get_buffer()->get_iter_at_line(location.range.start.line); auto end = start; end.forward_to_line_end(); usage.second = Glib::Markup::escape_text((*view_it)->get_buffer()->get_text(start, end)); - embolden_token(usage.second, usage.first.index, end_offsets[c].index); + embolden_token(usage.second, location.range.start.character, location.range.end.character); } } else { - auto it = file_lines.find(usage.first.file_path); + auto it = file_lines.find(location.uri); if(it == file_lines.end()) { - std::ifstream ifs(usage.first.file_path.string()); + std::ifstream ifs(location.uri); if(ifs) { std::vector lines; std::string line; @@ -656,23 +640,23 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() { line.pop_back(); lines.emplace_back(line); } - auto pair = file_lines.emplace(usage.first.file_path, lines); + auto pair = file_lines.emplace(location.uri, lines); it = pair.first; } else { - auto pair = file_lines.emplace(usage.first.file_path, std::vector()); + auto pair = file_lines.emplace(location.uri, std::vector()); it = pair.first; } } - if(usage.first.line < it->second.size()) { - usage.second = it->second[usage.first.line]; - embolden_token(usage.second, usage.first.index, end_offsets[c].index); + if(location.range.start.line < it->second.size()) { + usage.second = Glib::Markup::escape_text(it->second[location.range.start.line]); + embolden_token(usage.second, location.range.start.character, location.range.end.character); } } } - if(usages.empty()) + if(locations.empty()) Info::get().print("No symbol found at current cursor location"); return usages; @@ -698,11 +682,10 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() { if(capabilities.rename || capabilities.document_highlight) { rename_similar_tokens = [this](const std::string &text) { - class Usages { + class Changes { public: - boost::filesystem::path path; - std::unique_ptr new_text; - std::vector> offsets; + std::string uri; + std::vector text_edits; }; auto previous_text = get_token_spelling(); @@ -710,10 +693,10 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() { return; auto iter = get_buffer()->get_insert()->get_iter(); - std::vector usages; + std::vector changes; std::promise result_processed; if(capabilities.rename) { - client->write_request(this, "textDocument/rename", R"("textDocument":{"uri":")" + uri + R"("}, "position": {"line": )" + std::to_string(iter.get_line()) + ", \"character\": " + std::to_string(iter.get_line_offset()) + R"(}, "newName": ")" + text + "\"", [this, &usages, &result_processed](const boost::property_tree::ptree &result, bool error) { + client->write_request(this, "textDocument/rename", R"("textDocument":{"uri":"file://)" + file_path.string() + R"("}, "position": {"line": )" + std::to_string(iter.get_line()) + ", \"character\": " + std::to_string(iter.get_line_offset()) + R"(}, "newName": ")" + text + "\"", [this, &changes, &result_processed](const boost::property_tree::ptree &result, bool error) { if(!error) { boost::filesystem::path project_path; auto build = Project::Build::create(file_path); @@ -725,22 +708,13 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() { auto changes_it = result.find("changes"); if(changes_it != result.not_found()) { for(auto file_it = changes_it->second.begin(); file_it != changes_it->second.end(); ++file_it) { - auto path = file_it->first; - if(path.size() >= 7) { - path.erase(0, 7); - if(filesystem::file_in_path(path, project_path)) { - usages.emplace_back(Usages{path, nullptr, std::vector>()}); - for(auto edit_it = file_it->second.begin(); edit_it != file_it->second.end(); ++edit_it) { - auto range_it = edit_it->second.find("range"); - if(range_it != edit_it->second.not_found()) { - auto start_it = range_it->second.find("start"); - auto end_it = range_it->second.find("end"); - if(start_it != range_it->second.not_found() && end_it != range_it->second.not_found()) - usages.back().offsets.emplace_back(std::make_pair(Offset(start_it->second.get("line"), start_it->second.get("character")), - Offset(end_it->second.get("line"), end_it->second.get("character")))); - } - } - } + auto uri = file_it->first; + uri.erase(0, 7); + if(filesystem::file_in_path(uri, project_path)) { + std::vector edits; + for(auto edit_it = file_it->second.begin(); edit_it != file_it->second.end(); ++edit_it) + edits.emplace_back(edit_it->second); + changes.emplace_back(Changes{std::move(uri), std::move(edits)}); } } } @@ -749,62 +723,37 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() { for(auto change_it = changes_pt.begin(); change_it != changes_pt.end(); ++change_it) { auto document_it = change_it->second.find("textDocument"); if(document_it != change_it->second.not_found()) { - auto path = document_it->second.get("uri", ""); - if(path.size() >= 7) { - path.erase(0, 7); - if(filesystem::file_in_path(path, project_path)) { - usages.emplace_back(Usages{path, std::make_unique(), std::vector>()}); - auto edits_pt = change_it->second.get_child("edits", boost::property_tree::ptree()); - for(auto edit_it = edits_pt.begin(); edit_it != edits_pt.end(); ++edit_it) { - auto new_text_it = edit_it->second.find("newText"); - if(new_text_it != edit_it->second.not_found()) { - auto range_it = edit_it->second.find("range"); - if(range_it != edit_it->second.not_found()) { - auto start_it = range_it->second.find("start"); - auto end_it = range_it->second.find("end"); - if(start_it != range_it->second.not_found() && end_it != range_it->second.not_found()) { - auto end_line = end_it->second.get("line"); - if(end_line > std::numeric_limits::max()) - end_line = std::numeric_limits::max(); - *usages.back().new_text = new_text_it->second.get_value(); - usages.back().offsets.emplace_back(std::make_pair(Offset(start_it->second.get("line"), start_it->second.get("character")), - Offset(static_cast(end_line), end_it->second.get("character")))); - } - } - } - } - } + auto uri = document_it->second.get("uri", ""); + uri.erase(0, 7); + if(filesystem::file_in_path(uri, project_path)) { + std::vector edits; + auto edits_pt = change_it->second.get_child("edits", boost::property_tree::ptree()); + for(auto edit_it = edits_pt.begin(); edit_it != edits_pt.end(); ++edit_it) + edits.emplace_back(edit_it->second); + changes.emplace_back(Changes{std::move(uri), std::move(edits)}); } } } } } catch(...) { - usages.clear(); + changes.clear(); } } result_processed.set_value(); }); } else { - client->write_request(this, "textDocument/documentHighlight", R"("textDocument":{"uri":")" + uri + R"("}, "position": {"line": )" + std::to_string(iter.get_line()) + ", \"character\": " + std::to_string(iter.get_line_offset()) + R"(}, "context": {"includeDeclaration": true})", [this, &usages, &result_processed](const boost::property_tree::ptree &result, bool error) { + client->write_request(this, "textDocument/documentHighlight", R"("textDocument":{"uri":"file://)" + file_path.string() + R"("}, "position": {"line": )" + std::to_string(iter.get_line()) + ", \"character\": " + std::to_string(iter.get_line_offset()) + R"(}, "context": {"includeDeclaration": true})", [this, &changes, &text, &result_processed](const boost::property_tree::ptree &result, bool error) { if(!error) { try { - usages.emplace_back(Usages{file_path, nullptr, std::vector>()}); - for(auto it = result.begin(); it != result.end(); ++it) { - auto range_it = it->second.find("range"); - if(range_it != it->second.not_found()) { - auto start_it = range_it->second.find("start"); - auto end_it = range_it->second.find("end"); - if(start_it != range_it->second.not_found() && end_it != range_it->second.not_found()) { - usages.back().offsets.emplace_back(std::make_pair(Offset(start_it->second.get("line"), start_it->second.get("character")), - Offset(end_it->second.get("line"), end_it->second.get("character")))); - } - } - } + std::vector edits; + for(auto it = result.begin(); it != result.end(); ++it) + edits.emplace_back(it->second, text); + changes.emplace_back(Changes{file_path.string(), std::move(edits)}); } catch(...) { - usages.clear(); + changes.clear(); } } result_processed.set_value(); @@ -812,11 +761,11 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() { } result_processed.get_future().get(); - std::vector usages_renamed; - for(auto &usage : usages) { + std::vector changes_renamed; + for(auto &change : changes) { auto view_it = views.end(); for(auto it = views.begin(); it != views.end(); ++it) { - if((*it)->file_path == usage.path) { + if((*it)->file_path == change.uri) { view_it = it; break; } @@ -827,69 +776,63 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() { auto end_iter = buffer->end(); // If entire buffer is replaced - if(usage.new_text && usage.offsets.size() == 1 && - usage.offsets[0].first.line == 0 && usage.offsets[0].first.index == 0 && - (usage.offsets[0].second.line > static_cast(end_iter.get_line()) || - (usage.offsets[0].second.line == static_cast(end_iter.get_line()) && usage.offsets[0].second.index >= static_cast(end_iter.get_line_offset())))) - replace_text(*usage.new_text); + if(change.text_edits.size() == 1 && + change.text_edits[0].range.start.line == 0 && change.text_edits[0].range.start.character == 0 && + (change.text_edits[0].range.end.line > static_cast(end_iter.get_line()) || + (change.text_edits[0].range.end.line == static_cast(end_iter.get_line()) && change.text_edits[0].range.end.character >= static_cast(end_iter.get_line_offset())))) + replace_text(change.text_edits[0].new_text); else { - for(auto offset_it = usage.offsets.rbegin(); offset_it != usage.offsets.rend(); ++offset_it) { - auto start_iter = (*view_it)->get_iter_at_line_pos(offset_it->first.line, offset_it->first.index); - auto end_iter = (*view_it)->get_iter_at_line_pos(offset_it->second.line, offset_it->second.index); + for(auto edit_it = change.text_edits.rbegin(); edit_it != change.text_edits.rend(); ++edit_it) { + auto start_iter = (*view_it)->get_iter_at_line_pos(edit_it->range.start.line, edit_it->range.start.character); + auto end_iter = (*view_it)->get_iter_at_line_pos(edit_it->range.end.line, edit_it->range.end.character); buffer->erase(start_iter, end_iter); - start_iter = (*view_it)->get_iter_at_line_pos(offset_it->first.line, offset_it->first.index); - if(usage.new_text) - buffer->insert(start_iter, *usage.new_text); - else - buffer->insert(start_iter, text); + start_iter = (*view_it)->get_iter_at_line_pos(edit_it->range.start.line, edit_it->range.start.character); + buffer->insert(start_iter, edit_it->new_text); } } buffer->end_user_action(); (*view_it)->save(); - usages_renamed.emplace_back(&usage); + changes_renamed.emplace_back(&change); } else { Glib::ustring buffer; { - std::ifstream stream(usage.path.string(), std::ifstream::binary); + std::ifstream stream(change.uri, std::ifstream::binary); if(stream) buffer.assign(std::istreambuf_iterator(stream), std::istreambuf_iterator()); } - std::ofstream stream(usage.path.string(), std::ifstream::binary); + std::ofstream stream(change.uri, std::ifstream::binary); if(!buffer.empty() && stream) { std::vector lines_start_pos = {0}; for(size_t c = 0; c < buffer.size(); ++c) { if(buffer[c] == '\n') lines_start_pos.emplace_back(c + 1); } - for(auto offset_it = usage.offsets.rbegin(); offset_it != usage.offsets.rend(); ++offset_it) { - auto start_line = offset_it->first.line; - auto end_line = offset_it->second.line; + for(auto edit_it = change.text_edits.rbegin(); edit_it != change.text_edits.rend(); ++edit_it) { + auto start_line = edit_it->range.start.line; + auto end_line = edit_it->range.end.line; if(start_line < lines_start_pos.size()) { - auto start = lines_start_pos[start_line] + offset_it->first.index; + auto start = lines_start_pos[start_line] + edit_it->range.start.character; unsigned end; if(end_line >= lines_start_pos.size()) end = buffer.size(); else - end = lines_start_pos[end_line] + offset_it->second.index; + end = lines_start_pos[end_line] + edit_it->range.end.character; if(start < buffer.size() && end <= buffer.size()) { - if(usage.new_text) - buffer.replace(start, end - start, *usage.new_text); - else - buffer.replace(start, end - start, text); + buffer.replace(start, end - start, edit_it->new_text); } } } stream.write(buffer.data(), buffer.bytes()); - usages_renamed.emplace_back(&usage); + changes_renamed.emplace_back(&change); } else - Terminal::get().print("Error: could not write to file " + usage.path.string() + '\n', true); + Terminal::get().print("Error: could not write to file " + change.uri + '\n', true); } } - if(!usages_renamed.empty()) { + if(!changes_renamed.empty()) { Terminal::get().print("Renamed "); Terminal::get().print(previous_text, true); Terminal::get().print(" to "); @@ -926,41 +869,58 @@ void Source::LanguageProtocolView::escape_text(std::string &text) { } void Source::LanguageProtocolView::update_diagnostics(std::vector &&diagnostics) { - dispatcher.post([this, diagnostics = std::move(diagnostics)] { + dispatcher.post([this, diagnostics = std::move(diagnostics)]() mutable { clear_diagnostic_tooltips(); num_warnings = 0; num_errors = 0; num_fix_its = 0; for(auto &diagnostic : diagnostics) { - if(diagnostic.uri == uri) { - auto start = get_iter_at_line_pos(diagnostic.offsets.first.line, diagnostic.offsets.first.index); - auto end = get_iter_at_line_pos(diagnostic.offsets.second.line, diagnostic.offsets.second.index); + 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.is_end()) - end.forward_char(); - else - start.forward_char(); - } - - bool error = false; - std::string severity_tag_name; - if(diagnostic.severity >= 2) { - severity_tag_name = "def:warning"; - num_warnings++; - } - else { - severity_tag_name = "def:error"; - num_errors++; - error = true; - } + if(start == end) { + if(!end.is_end()) + end.forward_char(); + else + start.forward_char(); + } - add_diagnostic_tooltip(start, end, diagnostic.spelling, error); + bool error = false; + std::string severity_tag_name; + if(diagnostic.severity >= 2) { + severity_tag_name = "def:warning"; + num_warnings++; + } + else { + severity_tag_name = "def:error"; + num_errors++; + error = true; } + + add_diagnostic_tooltip(start, end, error, [this, diagnostic = std::move(diagnostic)](const Glib::RefPtr &buffer) { + buffer->insert_at_cursor(diagnostic.message); + + for(size_t i = 0; i < diagnostic.related_informations.size(); ++i) { + auto link = filesystem::get_relative_path(diagnostic.related_informations[i].location.uri, 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) + buffer->insert_at_cursor("\n\n"); + buffer->insert_at_cursor(diagnostic.related_informations[i].message); + buffer->insert_at_cursor(": "); + auto pos = buffer->get_insert()->get_iter(); + buffer->insert_with_tag(pos, link, "link"); + if(i != diagnostic.related_informations.size() - 1) + buffer->insert_at_cursor("\n"); + } + }); } for(auto &mark : flow_coverage_marks) - add_diagnostic_tooltip(mark.first->get_iter(), mark.second->get_iter(), flow_coverage_message, false); + add_diagnostic_tooltip(mark.first->get_iter(), mark.second->get_iter(), false, [](const Glib::RefPtr &buffer) { + buffer->insert_at_cursor(flow_coverage_message); + }); status_diagnostics = std::make_tuple(num_warnings + num_flow_coverage_warnings, num_errors, num_fix_its); if(update_status_diagnostics) @@ -1024,9 +984,16 @@ void Source::LanguageProtocolView::show_type_tooltips(const Gdk::Rectangle &rect if(offset >= get_buffer()->get_char_count()) return; type_tooltips.clear(); - auto create_tooltip_buffer = [this, offset, content]() { - auto tooltip_buffer = Gtk::TextBuffer::create(get_buffer()->get_tag_table()); - tooltip_buffer->insert(tooltip_buffer->get_insert()->get_iter(), *content); + + auto start = get_buffer()->get_iter_at_offset(offset); + auto end = start; + auto previous = start; + while(previous.backward_char() && ((*previous >= 'A' && *previous <= 'Z') || (*previous >= 'a' && *previous <= 'z') || (*previous >= '0' && *previous <= '9') || *previous == '_') && start.backward_char()) { + } + while(((*end >= 'A' && *end <= 'Z') || (*end >= 'a' && *end <= 'z') || (*end >= '0' && *end <= '9') || *end == '_') && end.forward_char()) { + } + type_tooltips.emplace_back(this, get_buffer()->create_mark(start), get_buffer()->create_mark(end), [this, offset, content](const Glib::RefPtr &buffer) { + buffer->insert(buffer->get_insert()->get_iter(), *content); #ifdef JUCI_ENABLE_DEBUG if(language_id == "rust" && capabilities.definition) { @@ -1056,24 +1023,13 @@ void Source::LanguageProtocolView::show_type_tooltips(const Gdk::Rectangle &rect next_char_iter++; debug_value.replace(iter, next_char_iter, "?"); } - tooltip_buffer->insert(tooltip_buffer->get_insert()->get_iter(), "\n\n" + value_type + ": " + debug_value.substr(pos + 3, debug_value.size() - (pos + 3) - 1)); + buffer->insert(buffer->get_insert()->get_iter(), "\n\n" + value_type + ": " + debug_value.substr(pos + 3, debug_value.size() - (pos + 3) - 1)); } } } } #endif - - return tooltip_buffer; - }; - - auto start = get_buffer()->get_iter_at_offset(offset); - auto end = start; - auto previous = start; - while(previous.backward_char() && ((*previous >= 'A' && *previous <= 'Z') || (*previous >= 'a' && *previous <= 'z') || (*previous >= '0' && *previous <= '9') || *previous == '_') && start.backward_char()) { - } - while(((*end >= 'A' && *end <= 'Z') || (*end >= 'a' && *end <= 'z') || (*end >= '0' && *end <= '9') || *end == '_') && end.forward_char()) { - } - type_tooltips.emplace_back(create_tooltip_buffer, this, get_buffer()->create_mark(start), get_buffer()->create_mark(end)); + }); type_tooltips.show(); }); } @@ -1095,33 +1051,24 @@ void Source::LanguageProtocolView::tag_similar_symbols() { static int request_count = 0; request_count++; auto current_request = request_count; - client->write_request(this, method, R"("textDocument":{"uri":")" + uri + R"("}, "position": {"line": )" + std::to_string(iter.get_line()) + ", \"character\": " + std::to_string(iter.get_line_offset()) + R"(}, "context": {"includeDeclaration": true})", [this, current_request](const boost::property_tree::ptree &result, bool error) { + client->write_request(this, method, R"("textDocument":{"uri":"file://)" + file_path.string() + R"("}, "position": {"line": )" + std::to_string(iter.get_line()) + ", \"character\": " + std::to_string(iter.get_line_offset()) + R"(}, "context": {"includeDeclaration": true})", [this, current_request](const boost::property_tree::ptree &result, bool error) { if(!error) { - std::vector> offsets; + std::vector ranges; for(auto it = result.begin(); it != result.end(); ++it) { - if(capabilities.document_highlight || it->second.get("uri", "") == uri) { - auto range_it = it->second.find("range"); - if(range_it != it->second.not_found()) { - auto start_it = range_it->second.find("start"); - auto end_it = range_it->second.find("end"); - if(start_it != range_it->second.not_found() && end_it != range_it->second.not_found()) { - try { - offsets.emplace_back(std::make_pair(Offset(start_it->second.get("line"), start_it->second.get("character")), - Offset(end_it->second.get("line"), end_it->second.get("character")))); - } - catch(...) { - } - } - } + try { + if(capabilities.document_highlight || it->second.get("uri") == file_path) + ranges.emplace_back(it->second.get_child("range")); + } + catch(...) { } } - dispatcher.post([this, offsets = std::move(offsets), current_request] { + dispatcher.post([this, ranges = std::move(ranges), current_request] { if(current_request != request_count) return; get_buffer()->remove_tag(similar_symbol_tag, get_buffer()->begin(), get_buffer()->end()); - for(auto &pair : offsets) { - auto start = get_iter_at_line_pos(pair.first.line, pair.first.index); - auto end = get_iter_at_line_pos(pair.second.line, pair.second.index); + 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); get_buffer()->apply_tag(similar_symbol_tag, start, end); } }); @@ -1132,19 +1079,18 @@ void Source::LanguageProtocolView::tag_similar_symbols() { Source::Offset Source::LanguageProtocolView::get_declaration(const Gtk::TextIter &iter) { auto offset = std::make_shared(); std::promise result_processed; - client->write_request(this, "textDocument/definition", R"("textDocument":{"uri":")" + uri + R"("}, "position": {"line": )" + std::to_string(iter.get_line()) + ", \"character\": " + std::to_string(iter.get_line_offset()) + "}", [offset, &result_processed](const boost::property_tree::ptree &result, bool error) { + client->write_request(this, "textDocument/definition", R"("textDocument":{"uri":"file://)" + file_path.string() + R"("}, "position": {"line": )" + std::to_string(iter.get_line()) + ", \"character\": " + std::to_string(iter.get_line_offset()) + "}", [offset, &result_processed](const boost::property_tree::ptree &result, bool error) { if(!error) { for(auto it = result.begin(); it != result.end(); ++it) { - auto uri = it->second.get("uri", ""); - if(uri.compare(0, 7, "file://") == 0) - uri.erase(0, 7); - auto range = it->second.find("range"); - if(range != it->second.not_found()) { - auto start = range->second.find("start"); - if(start != range->second.not_found()) - *offset = Offset(start->second.get("line", 0), start->second.get("character", 0), uri); + try { + LanguageProtocol::Location location(it->second); + offset->file_path = std::move(location.uri); + offset->line = location.range.start.line; + offset->index = location.range.start.character; + break; // TODO: can a language server return several definitions? + } + catch(...) { } - break; // TODO: can a language server return several definitions? } } result_processed.set_value(); @@ -1255,7 +1201,7 @@ void Source::LanguageProtocolView::setup_autocomplete() { autocomplete_comment.clear(); autocomplete_insert.clear(); std::promise result_processed; - client->write_request(this, "textDocument/completion", R"("textDocument":{"uri":")" + uri + R"("}, "position": {"line": )" + std::to_string(line_number - 1) + ", \"character\": " + std::to_string(column - 1) + "}", [this, &result_processed](const boost::property_tree::ptree &result, bool error) { + client->write_request(this, "textDocument/completion", R"("textDocument":{"uri":"file://)" + file_path.string() + R"("}, "position": {"line": )" + std::to_string(line_number - 1) + ", \"character\": " + std::to_string(column - 1) + "}", [this, &result_processed](const boost::property_tree::ptree &result, bool error) { if(!error) { auto begin = result.begin(); // rust language server is bugged auto end = result.end(); @@ -1445,7 +1391,9 @@ void Source::LanguageProtocolView::add_flow_coverage_tooltips(bool called_in_thr auto end_pt = it->second.get_child("end"); auto end = get_iter_at_line_offset(end_pt.get("line") - 1, end_pt.get("column")); - add_diagnostic_tooltip(start, end, flow_coverage_message, false); + add_diagnostic_tooltip(start, end, false, [](const Glib::RefPtr &buffer) { + buffer->insert_at_cursor(flow_coverage_message); + }); ++num_flow_coverage_warnings; flow_coverage_marks.emplace_back(get_buffer()->create_mark(start), get_buffer()->create_mark(end)); diff --git a/src/source_language_protocol.h b/src/source_language_protocol.h index a532eae..61615cf 100644 --- a/src/source_language_protocol.h +++ b/src/source_language_protocol.h @@ -14,12 +14,65 @@ namespace Source { } namespace LanguageProtocol { + class Offset { + public: + Offset(const boost::property_tree::ptree &pt) : line(pt.get("line")), + character(pt.get("character")) {} + unsigned line; + unsigned character; + }; + + class Range { + public: + Range(const boost::property_tree::ptree &pt) : start(pt.get_child("start")), + end(pt.get_child("end")) {} + Range() = default; + Offset start, end; + }; + + class Location { + public: + Location(const boost::property_tree::ptree &pt, std::string uri_ = {}) : range(pt.get_child("range")) { + if(uri_.empty()) { + uri = pt.get("uri"); + uri.erase(0, 7); + } + else + uri = std::move(uri_); + } + std::string uri; + Range range; + }; + class Diagnostic { public: - std::string spelling; - std::pair offsets; + class RelatedInformation { + public: + RelatedInformation(const boost::property_tree::ptree &pt) : message(pt.get("message")), + location(pt.get_child("location")) {} + std::string message; + Location location; + }; + + Diagnostic(const boost::property_tree::ptree &pt) : message(pt.get("message")), + range(pt.get_child("range")), + severity(pt.get("severity", 0)) { + auto related_information_it = pt.get_child("relatedInformation", boost::property_tree::ptree()); + for(auto it = related_information_it.begin(); it != related_information_it.end(); ++it) + related_informations.emplace_back(it->second); + } + std::string message; + Range range; unsigned severity; - std::string uri; + std::vector related_informations; + }; + + class TextEdit { + public: + TextEdit(const boost::property_tree::ptree &pt, std::string new_text_ = {}) : range(pt.get_child("range")), + new_text(new_text_.empty() ? pt.get("newText") : std::move(new_text_)) {} + Range range; + std::string new_text; }; class Capabilities { @@ -86,9 +139,11 @@ namespace Source { class LanguageProtocolView : public View { public: LanguageProtocolView(const boost::filesystem::path &file_path, const Glib::RefPtr &language, std::string language_id_); + void initialize(bool setup); + void close(); ~LanguageProtocolView() override; - std::string uri; + void rename(const boost::filesystem::path &path) override; bool save() override; void update_diagnostics(std::vector &&diagnostics); @@ -125,7 +180,6 @@ namespace Source { boost::filesystem::path flow_coverage_executable; std::vector, Glib::RefPtr>> flow_coverage_marks; - const std::string flow_coverage_message = "Not covered by Flow"; size_t num_warnings = 0, num_errors = 0, num_fix_its = 0, num_flow_coverage_warnings = 0; void add_flow_coverage_tooltips(bool called_in_thread); }; diff --git a/src/terminal.cc b/src/terminal.cc index 3f85e41..a397dab 100644 --- a/src/terminal.cc +++ b/src/terminal.cc @@ -354,22 +354,10 @@ bool Terminal::on_button_press_event(GdkEventButton *button_event) { } auto link = find_link(get_buffer()->get_text(start_iter, end_iter).raw()); if(std::get<0>(link) != static_cast(-1)) { - boost::filesystem::path path = std::get<2>(link); + auto path = filesystem::get_long_path(std::get<2>(link)); std::string line = std::get<3>(link); std::string index = std::get<4>(link); - if(!path.empty() && *path.begin() == "~") { // boost::filesystem does not recognize ~ - boost::filesystem::path corrected_path; - corrected_path = filesystem::get_home_path(); - if(!corrected_path.empty()) { - auto it = path.begin(); - ++it; - for(; it != path.end(); ++it) - corrected_path /= *it; - path = corrected_path; - } - } - if(path.is_relative()) { if(Project::current) { if(boost::filesystem::exists(Project::current->build->get_default_path() / path)) diff --git a/src/tooltips.cc b/src/tooltips.cc index cf40c3c..9d4e72b 100644 --- a/src/tooltips.cc +++ b/src/tooltips.cc @@ -1,12 +1,15 @@ #include "tooltips.h" +#include "filesystem.h" +#include "notebook.h" #include "selection_dialog.h" +#include std::set Tooltips::shown_tooltips; Gdk::Rectangle Tooltips::drawn_tooltips_rectangle = Gdk::Rectangle(); -Tooltip::Tooltip(std::function()> create_tooltip_buffer_, Gtk::TextView *text_view, - Glib::RefPtr start_mark_, Glib::RefPtr end_mark_) - : start_mark(std::move(start_mark_)), end_mark(std::move(end_mark_)), create_tooltip_buffer(std::move(create_tooltip_buffer_)), text_view(text_view) {} +Tooltip::Tooltip(Gtk::TextView *text_view, Glib::RefPtr start_mark_, + Glib::RefPtr end_mark_, std::function &)> set_buffer_) + : start_mark(std::move(start_mark_)), end_mark(std::move(end_mark_)), text_view(text_view), set_buffer(std::move(set_buffer_)) {} Tooltip::~Tooltip() { Tooltips::shown_tooltips.erase(this); @@ -70,13 +73,76 @@ void Tooltip::show(bool disregard_drawn, const std::function &on_motion) box->get_style_context()->add_class("juci_tooltip_box"); window->add(*box); - text_buffer = create_tooltip_buffer(); + if(text_view) { + text_buffer = Gtk::TextBuffer::create(text_view->get_buffer()->get_tag_table()); + link_tag = text_buffer->get_tag_table()->lookup("link"); + } + else { + text_buffer = Gtk::TextBuffer::create(); + link_tag = text_buffer->create_tag("link"); + link_tag->property_underline() = Pango::Underline::UNDERLINE_SINGLE; + link_tag->property_foreground_rgba() = window->get_style_context()->get_color(Gtk::StateFlags::STATE_FLAG_LINK); + } + set_buffer(text_buffer); + wrap_lines(); auto tooltip_text_view = Gtk::manage(new Gtk::TextView(text_buffer)); tooltip_text_view->get_style_context()->add_class("juci_tooltip_text_view"); tooltip_text_view->set_editable(false); + static auto link_mouse_cursor = Gdk::Cursor::create(Gdk::CursorType::HAND1); + static auto default_mouse_cursor = Gdk::Cursor::create(Gdk::CursorType::XTERM); + tooltip_text_view->signal_motion_notify_event().connect([this, tooltip_text_view](GdkEventMotion *event) { + Gtk::TextIter iter; + int location_x, location_y; + tooltip_text_view->window_to_buffer_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, event->x, event->y, location_x, location_y); + tooltip_text_view->get_iter_at_location(iter, location_x, location_y); + if(iter.has_tag(link_tag)) + tooltip_text_view->get_window(Gtk::TextWindowType::TEXT_WINDOW_TEXT)->set_cursor(link_mouse_cursor); + else + tooltip_text_view->get_window(Gtk::TextWindowType::TEXT_WINDOW_TEXT)->set_cursor(default_mouse_cursor); + return false; + }); + tooltip_text_view->signal_button_press_event().connect([this, tooltip_text_view](GdkEventButton *event) { + if(event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_PRIMARY) { + Gtk::TextIter iter; + int location_x, location_y; + tooltip_text_view->window_to_buffer_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, event->x, event->y, location_x, location_y); + tooltip_text_view->get_iter_at_location(iter, location_x, location_y); + if(iter.has_tag(link_tag)) { + auto start = iter; + start.backward_to_tag_toggle(link_tag); + auto end = iter; + end.forward_to_tag_toggle(link_tag); + std::string text = tooltip_text_view->get_buffer()->get_text(start, end); + static std::regex regex("^([^:]+):([^:]+):([^:]+)$"); + std::smatch sm; + if(std::regex_match(text, sm, regex)) { + auto path = boost::filesystem::path(sm[1].str()); + if(auto view = dynamic_cast(text_view)) + path = filesystem::get_normal_path(view->file_path.parent_path() / path); + + if(boost::filesystem::is_regular_file(path)) { + Notebook::get().open(path); + if(auto view = Notebook::get().get_current_view()) { + try { + auto line = atoi(sm[2].str().c_str()) - 1; + auto offset = atoi(sm[3].str().c_str()) - 1; + view->place_cursor_at_line_offset(line, offset); + view->scroll_to_cursor_delayed(view, true, false); + } + catch(...) { + } + } + return true; + } + } + } + } + return false; + }); + #if GTK_VERSION_GE(3, 20) box->add(*tooltip_text_view); #else diff --git a/src/tooltips.h b/src/tooltips.h index 5a57505..5d7dab4 100644 --- a/src/tooltips.h +++ b/src/tooltips.h @@ -7,8 +7,8 @@ class Tooltip { public: - Tooltip(std::function()> create_tooltip_buffer_, Gtk::TextView *text_view, Glib::RefPtr start_mark_, Glib::RefPtr end_mark_); - Tooltip(std::function()> create_tooltip_buffer_) : Tooltip(std::move(create_tooltip_buffer_), nullptr, Glib::RefPtr(), Glib::RefPtr()) {} + Tooltip(Gtk::TextView *text_view, Glib::RefPtr start_mark_, Glib::RefPtr end_mark_, std::function &)> set_buffer_); + Tooltip(std::function &)> set_buffer_) : Tooltip(nullptr, Glib::RefPtr(), Glib::RefPtr(), std::move(set_buffer_)) {} ~Tooltip(); void update(); @@ -25,12 +25,14 @@ private: std::unique_ptr window; void wrap_lines(); - std::function()> create_tooltip_buffer; Gtk::TextView *text_view; + std::function &)> set_buffer; std::pair size; Gdk::Rectangle rectangle; bool shown = false; + + Glib::RefPtr link_tag; }; class Tooltips { diff --git a/src/window.cc b/src/window.cc index 947f5c3..b5dc824 100644 --- a/src/window.cc +++ b/src/window.cc @@ -817,8 +817,6 @@ void Window::set_menu_actions() { } for(auto &location : locations) { auto path = filesystem::get_relative_path(filesystem::get_normal_path(location.file_path), project_path); - if(path.empty()) - path = location.file_path.filename(); auto row = path.string() + ":" + std::to_string(location.line + 1); rows.emplace_back(location); SelectionDialog::get()->add_row(row); diff --git a/tests/filesystem_test.cc b/tests/filesystem_test.cc index 5940918..57b732b 100644 --- a/tests/filesystem_test.cc +++ b/tests/filesystem_test.cc @@ -78,5 +78,10 @@ int main() { { boost::filesystem::path relative_path = "filesystem_test.cc"; g_assert(filesystem::get_relative_path(tests_path / relative_path, tests_path) == relative_path); + g_assert(filesystem::get_relative_path(tests_path / "test" / relative_path, tests_path) == boost::filesystem::path("test") / relative_path); + + g_assert(filesystem::get_relative_path("/test/test/test.cc", "/test/base") == boost::filesystem::path("..") / "test" / "test.cc"); + g_assert(filesystem::get_relative_path("/test/test/test/test.cc", "/test/base") == boost::filesystem::path("..") / "test" / "test" / "test.cc"); + g_assert(filesystem::get_relative_path("/test2/test.cc", "/test/base") == boost::filesystem::path("..") / ".." / "test2" / "test.cc"); } } \ No newline at end of file diff --git a/tests/stubs/tooltips.cc b/tests/stubs/tooltips.cc index 18b367b..41525d2 100644 --- a/tests/stubs/tooltips.cc +++ b/tests/stubs/tooltips.cc @@ -2,10 +2,9 @@ Gdk::Rectangle Tooltips::drawn_tooltips_rectangle = Gdk::Rectangle(); -Tooltip::Tooltip(std::function()> create_tooltip_buffer, - Gtk::TextView *text_view, - Glib::RefPtr start_mark, - Glib::RefPtr end_mark) : text_view(text_view) {} +Tooltip::Tooltip(Gtk::TextView *text_view, + Glib::RefPtr start_mark, Glib::RefPtr end_mark, + std::function &)> create_tooltip_buffer) : text_view(text_view) {} Tooltip::~Tooltip() {}