Browse Source

Language client: now supports multiple declarations and type declarations

merge-requests/413/head
eidheim 4 years ago
parent
commit
00a696d19c
  1. 4
      src/source.hpp
  2. 21
      src/source_clang.cpp
  3. 60
      src/source_language_protocol.cpp
  4. 4
      src/source_language_protocol.hpp
  5. 54
      src/window.cpp
  6. 30
      tests/language_protocol_client_test.cpp
  7. 21
      tests/source_clang_test.cpp

4
src/source.hpp

@ -85,8 +85,8 @@ namespace Source {
std::function<void()> non_interactive_completion; std::function<void()> non_interactive_completion;
std::function<void(bool, bool)> format_style; std::function<void(bool, bool)> format_style;
std::function<Offset()> get_declaration_location; std::function<std::vector<Offset>()> get_declaration_locations;
std::function<Offset()> get_type_declaration_location; std::function<std::vector<Offset>()> get_type_declaration_locations;
std::function<std::vector<Offset>()> get_implementation_locations; std::function<std::vector<Offset>()> get_implementation_locations;
std::function<std::vector<Offset>()> get_declaration_or_implementation_locations; std::function<std::vector<Offset>()> get_declaration_or_implementation_locations;
std::function<std::vector<std::pair<Offset, std::string>>()> get_usages; std::function<std::vector<std::pair<Offset, std::string>>()> get_usages;

21
src/source_clang.cpp

@ -1421,7 +1421,7 @@ Source::ClangViewRefactor::ClangViewRefactor(const boost::filesystem::path &file
return Offset(); return Offset();
}; };
get_declaration_location = [this, declaration_location]() { get_declaration_locations = [this, declaration_location]() -> std::vector<Offset> {
if(!parsed) { if(!parsed) {
if(selected_completion_string) { if(selected_completion_string) {
auto completion_cursor = clangmm::CompletionString(selected_completion_string).get_cursor(clang_tu->cx_tu); auto completion_cursor = clangmm::CompletionString(selected_completion_string).get_cursor(clang_tu->cx_tu);
@ -1439,16 +1439,16 @@ Source::ClangViewRefactor::ClangViewRefactor(const boost::filesystem::path &file
if(!boost::filesystem::exists(include_path, ec)) if(!boost::filesystem::exists(include_path, ec))
offset.file_path = "/usr/include" / include_path; offset.file_path = "/usr/include" / include_path;
return offset; return {offset};
} }
else { else {
Info::get().print("No declaration found"); Info::get().print("No declaration found");
return Offset(); return {};
} }
} }
Info::get().print("Buffer is parsing"); Info::get().print("Buffer is parsing");
return Offset(); return {};
} }
auto offset = declaration_location(); auto offset = declaration_location();
if(!offset) if(!offset)
@ -1461,13 +1461,13 @@ Source::ClangViewRefactor::ClangViewRefactor(const boost::filesystem::path &file
if(!boost::filesystem::exists(include_path, ec)) if(!boost::filesystem::exists(include_path, ec))
offset.file_path = "/usr/include" / include_path; offset.file_path = "/usr/include" / include_path;
return offset; return {offset};
}; };
get_type_declaration_location = [this]() { get_type_declaration_locations = [this]() -> std::vector<Offset> {
if(!parsed) { if(!parsed) {
Info::get().print("Buffer is parsing"); Info::get().print("Buffer is parsing");
return Offset(); return {};
} }
auto identifier = get_identifier(); auto identifier = get_identifier();
if(identifier) { if(identifier) {
@ -1486,12 +1486,12 @@ Source::ClangViewRefactor::ClangViewRefactor(const boost::filesystem::path &file
if(!boost::filesystem::exists(include_path, ec)) if(!boost::filesystem::exists(include_path, ec))
offset.file_path = "/usr/include" / include_path; offset.file_path = "/usr/include" / include_path;
return offset; return {offset};
} }
} }
} }
Info::get().print("No type declaration found"); Info::get().print("No type declaration found");
return Offset(); return {};
}; };
auto implementation_locations = [this](const Identifier &identifier) { auto implementation_locations = [this](const Identifier &identifier) {
@ -1658,9 +1658,8 @@ Source::ClangViewRefactor::ClangViewRefactor(const boost::filesystem::path &file
} }
else { else {
auto implementation_offsets = implementation_locations(get_identifier()); auto implementation_offsets = implementation_locations(get_identifier());
if(!implementation_offsets.empty()) { if(!implementation_offsets.empty())
offsets = std::move(implementation_offsets); offsets = std::move(implementation_offsets);
}
else { else {
auto offset = declaration_location(); auto offset = declaration_location();
if(offset) if(offset)

60
src/source_language_protocol.cpp

@ -927,11 +927,11 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() {
} }
if(capabilities.definition) { if(capabilities.definition) {
get_declaration_location = [this]() { get_declaration_locations = [this]() {
auto offset = get_declaration(get_buffer()->get_insert()->get_iter()); auto offsets = get_declarations(get_buffer()->get_insert()->get_iter());
if(!offset) if(offsets.empty())
Info::get().print("No declaration found"); Info::get().print("No declaration found");
return offset; return offsets;
}; };
// Assumes that capabilities.definition is available when capabilities.implementation is supported // Assumes that capabilities.definition is available when capabilities.implementation is supported
@ -949,27 +949,25 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() {
} }
} }
if(offsets.empty() || is_implementation) { if(offsets.empty() || is_implementation) {
if(auto offset = get_declaration_location()) auto offsets = get_declaration_locations();
return std::vector<Offset>({std::move(offset)}); if(!offsets.empty())
return offsets;
} }
return offsets; return offsets;
}; };
} }
else { else {
get_declaration_or_implementation_locations = [this]() { get_declaration_or_implementation_locations = [this]() {
std::vector<Offset> offsets; return get_declaration_locations();
if(auto offset = get_declaration_location())
offsets.emplace_back(std::move(offset));
return offsets;
}; };
} }
} }
if(capabilities.type_definition) { if(capabilities.type_definition) {
get_type_declaration_location = [this]() { get_type_declaration_locations = [this]() {
auto offset = get_type_declaration(get_buffer()->get_insert()->get_iter()); auto offsets = get_type_declarations(get_buffer()->get_insert()->get_iter());
if(!offset) if(offsets.empty())
Info::get().print("No type declaration found"); Info::get().print("No type declaration found");
return offset; return offsets;
}; };
} }
if(capabilities.implementation) { if(capabilities.implementation) {
@ -2249,10 +2247,12 @@ void Source::LanguageProtocolView::show_type_tooltips(const Gdk::Rectangle &rect
Glib::ustring value_type = "Value"; Glib::ustring value_type = "Value";
auto token_iters = get_token_iters(get_buffer()->get_iter_at_offset(offset)); auto token_iters = get_token_iters(get_buffer()->get_iter_at_offset(offset));
auto offset = get_declaration(token_iters.first);
auto variable = get_buffer()->get_text(token_iters.first, token_iters.second); auto variable = get_buffer()->get_text(token_iters.first, token_iters.second);
Glib::ustring debug_value = Debug::LLDB::get().get_value(variable, offset.file_path, offset.line + 1, offset.index + 1); Glib::ustring debug_value;
auto offsets = get_declarations(token_iters.first);
for(size_t i = 0; i < offsets.size() && debug_value.empty(); ++i)
debug_value = Debug::LLDB::get().get_value(variable, offsets[i].file_path, offsets[i].line + 1, offsets[i].index + 1);
if(debug_value.empty()) { if(debug_value.empty()) {
debug_value = Debug::LLDB::get().get_return_value(file_path, token_iters.first.get_line() + 1, token_iters.first.get_line_index() + 1); debug_value = Debug::LLDB::get().get_return_value(file_path, token_iters.first.get_line() + 1, token_iters.first.get_line_index() + 1);
if(!debug_value.empty()) if(!debug_value.empty())
@ -2353,10 +2353,10 @@ void Source::LanguageProtocolView::apply_clickable_tag(const Gtk::TextIter &iter
}); });
} }
Source::Offset Source::LanguageProtocolView::get_declaration(const Gtk::TextIter &iter) { std::vector<Source::Offset> Source::LanguageProtocolView::get_declarations(const Gtk::TextIter &iter) {
auto offset = std::make_shared<Offset>(); auto offsets = std::make_shared<std::vector<Offset>>();
std::promise<void> result_processed; std::promise<void> result_processed;
write_request("textDocument/definition", to_string({make_position(iter.get_line(), get_line_pos(iter))}), [offset, &result_processed](JSON &&result, bool error) { write_request("textDocument/definition", to_string({make_position(iter.get_line(), get_line_pos(iter))}), [offsets, &result_processed](JSON &&result, bool error) {
if(!error) { if(!error) {
auto locations = result.array_or_empty(); auto locations = result.array_or_empty();
if(locations.empty()) { if(locations.empty()) {
@ -2366,10 +2366,7 @@ Source::Offset Source::LanguageProtocolView::get_declaration(const Gtk::TextIter
for(auto &location_object : locations) { for(auto &location_object : locations) {
try { try {
LanguageProtocol::Location location(location_object); LanguageProtocol::Location location(location_object);
offset->file_path = std::move(location.file); offsets->emplace_back(location.range.start.line, location.range.start.character, std::move(location.file));
offset->line = location.range.start.line;
offset->index = location.range.start.character;
break; // TODO: can a language server return several definitions?
} }
catch(...) { catch(...) {
} }
@ -2378,13 +2375,13 @@ Source::Offset Source::LanguageProtocolView::get_declaration(const Gtk::TextIter
result_processed.set_value(); result_processed.set_value();
}); });
result_processed.get_future().get(); result_processed.get_future().get();
return *offset; return *offsets;
} }
Source::Offset Source::LanguageProtocolView::get_type_declaration(const Gtk::TextIter &iter) { std::vector<Source::Offset> Source::LanguageProtocolView::get_type_declarations(const Gtk::TextIter &iter) {
auto offset = std::make_shared<Offset>(); auto offsets = std::make_shared<std::vector<Offset>>();
std::promise<void> result_processed; std::promise<void> result_processed;
write_request("textDocument/typeDefinition", to_string({make_position(iter.get_line(), get_line_pos(iter))}), [offset, &result_processed](JSON &&result, bool error) { write_request("textDocument/typeDefinition", to_string({make_position(iter.get_line(), get_line_pos(iter))}), [offsets, &result_processed](JSON &&result, bool error) {
if(!error) { if(!error) {
auto locations = result.array_or_empty(); auto locations = result.array_or_empty();
if(locations.empty()) { if(locations.empty()) {
@ -2394,10 +2391,7 @@ Source::Offset Source::LanguageProtocolView::get_type_declaration(const Gtk::Tex
for(auto &location_object : locations) { for(auto &location_object : locations) {
try { try {
LanguageProtocol::Location location(location_object); LanguageProtocol::Location location(location_object);
offset->file_path = std::move(location.file); offsets->emplace_back(location.range.start.line, location.range.start.character, std::move(location.file));
offset->line = location.range.start.line;
offset->index = location.range.start.character;
break; // TODO: can a language server return several type definitions?
} }
catch(...) { catch(...) {
} }
@ -2406,7 +2400,7 @@ Source::Offset Source::LanguageProtocolView::get_type_declaration(const Gtk::Tex
result_processed.set_value(); result_processed.set_value();
}); });
result_processed.get_future().get(); result_processed.get_future().get();
return *offset; return *offsets;
} }
std::vector<Source::Offset> Source::LanguageProtocolView::get_implementations(const Gtk::TextIter &iter) { std::vector<Source::Offset> Source::LanguageProtocolView::get_implementations(const Gtk::TextIter &iter) {
@ -2422,7 +2416,7 @@ std::vector<Source::Offset> Source::LanguageProtocolView::get_implementations(co
for(auto &location_object : locations) { for(auto &location_object : locations) {
try { try {
LanguageProtocol::Location location(location_object); LanguageProtocol::Location location(location_object);
offsets->emplace_back(location.range.start.line, location.range.start.character, location.file); offsets->emplace_back(location.range.start.line, location.range.start.character, std::move(location.file));
} }
catch(...) { catch(...) {
} }

4
src/source_language_protocol.hpp

@ -267,8 +267,8 @@ namespace Source {
void tag_similar_symbols(); void tag_similar_symbols();
Offset get_declaration(const Gtk::TextIter &iter); std::vector<Offset> get_declarations(const Gtk::TextIter &iter);
Offset get_type_declaration(const Gtk::TextIter &iter); std::vector<Offset> get_type_declarations(const Gtk::TextIter &iter);
std::vector<Offset> get_implementations(const Gtk::TextIter &iter); std::vector<Offset> get_implementations(const Gtk::TextIter &iter);
std::unique_ptr<Autocomplete> autocomplete; std::unique_ptr<Autocomplete> autocomplete;

54
src/window.cpp

@ -1128,44 +1128,6 @@ void Window::set_menu_actions() {
} }
}); });
menu.add_action("source_goto_declaration", []() {
if(auto view = Notebook::get().get_current_view()) {
if(view->get_declaration_location) {
auto location = view->get_declaration_location();
if(location) {
boost::system::error_code ec;
if(!boost::filesystem::is_regular_file(location.file_path, ec))
return;
if(Notebook::get().open(location.file_path)) {
auto view = Notebook::get().get_current_view();
auto line = static_cast<int>(location.line);
auto index = static_cast<int>(location.index);
view->place_cursor_at_line_pos(line, index);
view->scroll_to_cursor_delayed(true, false);
}
}
}
}
});
menu.add_action("source_goto_type_declaration", []() {
if(auto view = Notebook::get().get_current_view()) {
if(view->get_type_declaration_location) {
auto location = view->get_type_declaration_location();
if(location) {
boost::system::error_code ec;
if(!boost::filesystem::is_regular_file(location.file_path, ec))
return;
if(Notebook::get().open(location.file_path)) {
auto view = Notebook::get().get_current_view();
auto line = static_cast<int>(location.line);
auto index = static_cast<int>(location.index);
view->place_cursor_at_line_pos(line, index);
view->scroll_to_cursor_delayed(true, false);
}
}
}
}
});
auto goto_selected_location = [](Source::View *view, const std::vector<Source::Offset> &locations) { auto goto_selected_location = [](Source::View *view, const std::vector<Source::Offset> &locations) {
if(!locations.empty()) { if(!locations.empty()) {
SelectionDialog::create(view, true, true); SelectionDialog::create(view, true, true);
@ -1215,6 +1177,18 @@ void Window::set_menu_actions() {
SelectionDialog::get()->show(); SelectionDialog::get()->show();
} }
}; };
menu.add_action("source_goto_declaration", [goto_selected_location]() {
if(auto view = Notebook::get().get_current_view()) {
if(view->get_declaration_locations)
goto_selected_location(view, view->get_declaration_locations());
}
});
menu.add_action("source_goto_type_declaration", [goto_selected_location]() {
if(auto view = Notebook::get().get_current_view()) {
if(view->get_type_declaration_locations)
goto_selected_location(view, view->get_type_declaration_locations());
}
});
menu.add_action("source_goto_implementation", [goto_selected_location]() { menu.add_action("source_goto_implementation", [goto_selected_location]() {
if(auto view = Notebook::get().get_current_view()) { if(auto view = Notebook::get().get_current_view()) {
if(view->get_implementation_locations) if(view->get_implementation_locations)
@ -1812,8 +1786,8 @@ void Window::set_menu_actions() {
menu.actions["source_comments_toggle"]->set_enabled(view && view->toggle_comments); menu.actions["source_comments_toggle"]->set_enabled(view && view->toggle_comments);
menu.actions["source_comments_add_documentation"]->set_enabled(view && view->get_documentation_template); menu.actions["source_comments_add_documentation"]->set_enabled(view && view->get_documentation_template);
menu.actions["source_find_documentation"]->set_enabled(view && view->get_token_data); menu.actions["source_find_documentation"]->set_enabled(view && view->get_token_data);
menu.actions["source_goto_declaration"]->set_enabled(view && view->get_declaration_location); menu.actions["source_goto_declaration"]->set_enabled(view && view->get_declaration_locations);
menu.actions["source_goto_type_declaration"]->set_enabled(view && view->get_type_declaration_location); menu.actions["source_goto_type_declaration"]->set_enabled(view && view->get_type_declaration_locations);
menu.actions["source_goto_implementation"]->set_enabled(view && view->get_implementation_locations); menu.actions["source_goto_implementation"]->set_enabled(view && view->get_implementation_locations);
menu.actions["source_goto_declaration_or_implementation"]->set_enabled(view && view->get_declaration_or_implementation_locations); menu.actions["source_goto_declaration_or_implementation"]->set_enabled(view && view->get_declaration_or_implementation_locations);
menu.actions["source_goto_usage"]->set_enabled(view && view->get_usages); menu.actions["source_goto_usage"]->set_enabled(view && view->get_usages);

30
tests/language_protocol_client_test.cpp

@ -42,22 +42,24 @@ int main() {
} }
)"); )");
g_assert(view->get_declaration_location); g_assert(view->get_declaration_locations);
auto location = view->get_declaration_location(); auto locations = view->get_declaration_locations();
g_assert(location); g_assert(locations.size() == 1);
g_assert(location.file_path == "main.rs"); g_assert(locations[0]);
g_assert_cmpuint(location.line, ==, 0); g_assert(locations[0].file_path == "main.rs");
g_assert_cmpuint(location.index, ==, 3); g_assert_cmpuint(locations[0].line, ==, 0);
g_assert_cmpuint(locations[0].index, ==, 3);
g_assert(view->get_type_declaration_location);
location = view->get_type_declaration_location(); g_assert(view->get_type_declaration_locations);
g_assert(location); locations = view->get_type_declaration_locations();
g_assert(location.file_path == "main.rs"); g_assert(locations.size() == 1);
g_assert_cmpuint(location.line, ==, 0); g_assert(locations[0]);
g_assert_cmpuint(location.index, ==, 4); g_assert(locations[0].file_path == "main.rs");
g_assert_cmpuint(locations[0].line, ==, 0);
g_assert_cmpuint(locations[0].index, ==, 4);
g_assert(view->get_implementation_locations); g_assert(view->get_implementation_locations);
auto locations = view->get_implementation_locations(); locations = view->get_implementation_locations();
g_assert(locations.size() == 2); g_assert(locations.size() == 2);
g_assert(locations[0].file_path == "main.rs"); g_assert(locations[0].file_path == "main.rs");
g_assert(locations[1].file_path == "main.rs"); g_assert(locations[1].file_path == "main.rs");

21
tests/source_clang_test.cpp

@ -42,32 +42,37 @@ int main() {
//test get_declaration and get_implementation //test get_declaration and get_implementation
clang_view->place_cursor_at_line_index(15, 7); clang_view->place_cursor_at_line_index(15, 7);
auto location = clang_view->get_declaration_location(); auto locations = clang_view->get_declaration_locations();
g_assert_cmpuint(location.line, ==, 6); g_assert(locations.size() == 1);
g_assert_cmpuint(locations[0].line, ==, 6);
clang_view->place_cursor_at_line_index(location.line, location.index); clang_view->place_cursor_at_line_index(locations[0].line, locations[0].index);
auto impl_locations = clang_view->get_implementation_locations(); auto impl_locations = clang_view->get_implementation_locations();
g_assert_cmpuint(impl_locations.size(), ==, 1); g_assert_cmpuint(impl_locations.size(), ==, 1);
g_assert_cmpuint(impl_locations[0].line, ==, 11); g_assert_cmpuint(impl_locations[0].line, ==, 11);
clang_view->place_cursor_at_line_index(location.line, location.index); clang_view->place_cursor_at_line_index(locations[0].line, locations[0].index);
location = clang_view->get_declaration_location(); locations = clang_view->get_declaration_locations();
g_assert_cmpuint(location.line, ==, 6); g_assert(locations.size() == 1);
g_assert_cmpuint(locations[0].line, ==, 6);
//test get_usages and get_methods //test get_usages and get_methods
{
auto locations = clang_view->get_usages(); auto locations = clang_view->get_usages();
g_assert_cmpuint(locations.size(), >, 0); g_assert_cmpuint(locations.size(), >, 0);
locations = clang_view->get_methods(); locations = clang_view->get_methods();
g_assert_cmpuint(locations.size(), >, 0); g_assert_cmpuint(locations.size(), >, 0);
}
//Test rename class (error if not constructor and destructor is renamed as well) //Test rename class (error if not constructor and destructor is renamed as well)
auto saved_main = clang_view->get_buffer()->get_text(); auto saved_main = clang_view->get_buffer()->get_text();
clang_view->place_cursor_at_line_index(0, 6); clang_view->place_cursor_at_line_index(0, 6);
auto token = clang_view->get_token(clang_view->get_buffer()->get_insert()->get_iter()); auto token = clang_view->get_token(clang_view->get_buffer()->get_insert()->get_iter());
g_assert_cmpstr(token.c_str(), ==, "TestClass"); g_assert_cmpstr(token.c_str(), ==, "TestClass");
location = clang_view->get_declaration_location(); locations = clang_view->get_declaration_locations();
g_assert_cmpuint(location.line, ==, 0); g_assert(locations.size() == 1);
g_assert_cmpuint(locations[0].line, ==, 0);
clang_view->rename_similar_tokens("RenamedTestClass"); clang_view->rename_similar_tokens("RenamedTestClass");
while(!clang_view->parsed) while(!clang_view->parsed)
flush_events(); flush_events();

Loading…
Cancel
Save