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

60
src/source_language_protocol.cpp

@ -927,11 +927,11 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() {
}
if(capabilities.definition) {
get_declaration_location = [this]() {
auto offset = get_declaration(get_buffer()->get_insert()->get_iter());
if(!offset)
get_declaration_locations = [this]() {
auto offsets = get_declarations(get_buffer()->get_insert()->get_iter());
if(offsets.empty())
Info::get().print("No declaration found");
return offset;
return offsets;
};
// 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(auto offset = get_declaration_location())
return std::vector<Offset>({std::move(offset)});
auto offsets = get_declaration_locations();
if(!offsets.empty())
return offsets;
}
return offsets;
};
}
else {
get_declaration_or_implementation_locations = [this]() {
std::vector<Offset> offsets;
if(auto offset = get_declaration_location())
offsets.emplace_back(std::move(offset));
return offsets;
return get_declaration_locations();
};
}
}
if(capabilities.type_definition) {
get_type_declaration_location = [this]() {
auto offset = get_type_declaration(get_buffer()->get_insert()->get_iter());
if(!offset)
get_type_declaration_locations = [this]() {
auto offsets = get_type_declarations(get_buffer()->get_insert()->get_iter());
if(offsets.empty())
Info::get().print("No type declaration found");
return offset;
return offsets;
};
}
if(capabilities.implementation) {
@ -2249,10 +2247,12 @@ void Source::LanguageProtocolView::show_type_tooltips(const Gdk::Rectangle &rect
Glib::ustring value_type = "Value";
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);
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()) {
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())
@ -2353,10 +2353,10 @@ void Source::LanguageProtocolView::apply_clickable_tag(const Gtk::TextIter &iter
});
}
Source::Offset Source::LanguageProtocolView::get_declaration(const Gtk::TextIter &iter) {
auto offset = std::make_shared<Offset>();
std::vector<Source::Offset> Source::LanguageProtocolView::get_declarations(const Gtk::TextIter &iter) {
auto offsets = std::make_shared<std::vector<Offset>>();
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) {
auto locations = result.array_or_empty();
if(locations.empty()) {
@ -2366,10 +2366,7 @@ Source::Offset Source::LanguageProtocolView::get_declaration(const Gtk::TextIter
for(auto &location_object : locations) {
try {
LanguageProtocol::Location location(location_object);
offset->file_path = 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?
offsets->emplace_back(location.range.start.line, location.range.start.character, std::move(location.file));
}
catch(...) {
}
@ -2378,13 +2375,13 @@ Source::Offset Source::LanguageProtocolView::get_declaration(const Gtk::TextIter
result_processed.set_value();
});
result_processed.get_future().get();
return *offset;
return *offsets;
}
Source::Offset Source::LanguageProtocolView::get_type_declaration(const Gtk::TextIter &iter) {
auto offset = std::make_shared<Offset>();
std::vector<Source::Offset> Source::LanguageProtocolView::get_type_declarations(const Gtk::TextIter &iter) {
auto offsets = std::make_shared<std::vector<Offset>>();
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) {
auto locations = result.array_or_empty();
if(locations.empty()) {
@ -2394,10 +2391,7 @@ Source::Offset Source::LanguageProtocolView::get_type_declaration(const Gtk::Tex
for(auto &location_object : locations) {
try {
LanguageProtocol::Location location(location_object);
offset->file_path = 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?
offsets->emplace_back(location.range.start.line, location.range.start.character, std::move(location.file));
}
catch(...) {
}
@ -2406,7 +2400,7 @@ Source::Offset Source::LanguageProtocolView::get_type_declaration(const Gtk::Tex
result_processed.set_value();
});
result_processed.get_future().get();
return *offset;
return *offsets;
}
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) {
try {
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(...) {
}

4
src/source_language_protocol.hpp

@ -267,8 +267,8 @@ namespace Source {
void tag_similar_symbols();
Offset get_declaration(const Gtk::TextIter &iter);
Offset get_type_declaration(const Gtk::TextIter &iter);
std::vector<Offset> get_declarations(const Gtk::TextIter &iter);
std::vector<Offset> get_type_declarations(const Gtk::TextIter &iter);
std::vector<Offset> get_implementations(const Gtk::TextIter &iter);
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) {
if(!locations.empty()) {
SelectionDialog::create(view, true, true);
@ -1215,6 +1177,18 @@ void Window::set_menu_actions() {
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]() {
if(auto view = Notebook::get().get_current_view()) {
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_add_documentation"]->set_enabled(view && view->get_documentation_template);
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_type_declaration"]->set_enabled(view && view->get_type_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_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_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);
auto location = view->get_declaration_location();
g_assert(location);
g_assert(location.file_path == "main.rs");
g_assert_cmpuint(location.line, ==, 0);
g_assert_cmpuint(location.index, ==, 3);
g_assert(view->get_type_declaration_location);
location = view->get_type_declaration_location();
g_assert(location);
g_assert(location.file_path == "main.rs");
g_assert_cmpuint(location.line, ==, 0);
g_assert_cmpuint(location.index, ==, 4);
g_assert(view->get_declaration_locations);
auto locations = view->get_declaration_locations();
g_assert(locations.size() == 1);
g_assert(locations[0]);
g_assert(locations[0].file_path == "main.rs");
g_assert_cmpuint(locations[0].line, ==, 0);
g_assert_cmpuint(locations[0].index, ==, 3);
g_assert(view->get_type_declaration_locations);
locations = view->get_type_declaration_locations();
g_assert(locations.size() == 1);
g_assert(locations[0]);
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);
auto locations = view->get_implementation_locations();
locations = view->get_implementation_locations();
g_assert(locations.size() == 2);
g_assert(locations[0].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
clang_view->place_cursor_at_line_index(15, 7);
auto location = clang_view->get_declaration_location();
g_assert_cmpuint(location.line, ==, 6);
auto locations = clang_view->get_declaration_locations();
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();
g_assert_cmpuint(impl_locations.size(), ==, 1);
g_assert_cmpuint(impl_locations[0].line, ==, 11);
clang_view->place_cursor_at_line_index(location.line, location.index);
location = clang_view->get_declaration_location();
g_assert_cmpuint(location.line, ==, 6);
clang_view->place_cursor_at_line_index(locations[0].line, locations[0].index);
locations = clang_view->get_declaration_locations();
g_assert(locations.size() == 1);
g_assert_cmpuint(locations[0].line, ==, 6);
//test get_usages and get_methods
{
auto locations = clang_view->get_usages();
g_assert_cmpuint(locations.size(), >, 0);
locations = clang_view->get_methods();
g_assert_cmpuint(locations.size(), >, 0);
}
//Test rename class (error if not constructor and destructor is renamed as well)
auto saved_main = clang_view->get_buffer()->get_text();
clang_view->place_cursor_at_line_index(0, 6);
auto token = clang_view->get_token(clang_view->get_buffer()->get_insert()->get_iter());
g_assert_cmpstr(token.c_str(), ==, "TestClass");
location = clang_view->get_declaration_location();
g_assert_cmpuint(location.line, ==, 0);
locations = clang_view->get_declaration_locations();
g_assert(locations.size() == 1);
g_assert_cmpuint(locations[0].line, ==, 0);
clang_view->rename_similar_tokens("RenamedTestClass");
while(!clang_view->parsed)
flush_events();

Loading…
Cancel
Save