Browse Source

Language client: added support for RenameFile in textDocument/rename response

merge-requests/409/head
eidheim 4 years ago
parent
commit
46bc3de9d0
  1. 253
      src/source_language_protocol.cpp
  2. 7
      src/source_language_protocol.hpp
  3. 2
      tests/stubs/directories.cpp
  4. 2
      tests/stubs/notebook.cpp

253
src/source_language_protocol.cpp

@ -1,4 +1,5 @@
#include "source_language_protocol.hpp" #include "source_language_protocol.hpp"
#include "directories.hpp"
#include "filesystem.hpp" #include "filesystem.hpp"
#include "info.hpp" #include "info.hpp"
#include "notebook.hpp" #include "notebook.hpp"
@ -71,32 +72,42 @@ LanguageProtocol::WorkspaceEdit::WorkspaceEdit(const JSON &workspace_edit, boost
else else
project_path = file_path.parent_path(); project_path = file_path.parent_path();
try { try {
if(auto children = workspace_edit.children_optional("changes")) { if(auto changes = workspace_edit.children_optional("changes")) {
for(auto &child : *children) { for(auto &change : *changes) {
auto file = filesystem::get_path_from_uri(child.first); auto file = filesystem::get_path_from_uri(change.first);
if(filesystem::file_in_path(file, project_path)) { if(filesystem::file_in_path(file, project_path)) {
std::vector<LanguageProtocol::TextEdit> text_edits; std::vector<LanguageProtocol::TextEdit> text_edits;
for(auto &text_edit : child.second.array()) for(auto &text_edit : change.second.array())
text_edits.emplace_back(text_edit); text_edits.emplace_back(text_edit);
document_edits.emplace_back(file.string(), std::move(text_edits)); document_changes.emplace_back(TextDocumentEdit(file.string(), std::move(text_edits)));
} }
} }
} }
else if(auto children = workspace_edit.array_optional("documentChanges")) { else {
for(auto &child : *children) { auto document_changes = workspace_edit.array_or_empty("documentChanges");
LanguageProtocol::TextDocumentEdit document_edit(child); if(document_changes.empty()) {
if(filesystem::file_in_path(document_edit.file, project_path)) if(auto child = workspace_edit.object_optional("documentChanges"))
document_edits.emplace_back(std::move(document_edit.file), std::move(document_edit.text_edits)); document_changes.emplace_back(std::move(*child));
}
for(auto &document_change : document_changes) {
if(auto kind = document_change.string_optional("kind")) {
if(*kind == "rename") {
auto old_path = filesystem::get_path_from_uri(document_change.string("oldUri"));
auto new_path = filesystem::get_path_from_uri(document_change.string("newUri"));
if(filesystem::file_in_path(old_path, project_path) && filesystem::file_in_path(new_path, project_path))
this->document_changes.emplace_back(RenameFile{std::move(old_path), std::move(new_path)});
} }
} }
else if(auto child = workspace_edit.object_optional("documentChanges")) { else {
LanguageProtocol::TextDocumentEdit document_edit(*child); LanguageProtocol::TextDocumentEdit document_edit(document_change);
if(filesystem::file_in_path(document_edit.file, project_path)) if(filesystem::file_in_path(document_edit.file, project_path))
document_edits.emplace_back(std::move(document_edit.file), std::move(document_edit.text_edits)); this->document_changes.emplace_back(TextDocumentEdit(std::move(document_edit.file), std::move(document_edit.text_edits)));
}
}
} }
} }
catch(...) { catch(...) {
document_edits.clear(); document_changes.clear();
} }
} }
@ -544,51 +555,36 @@ void LanguageProtocol::Client::handle_server_request(size_t id, const std::strin
return; return;
} }
struct DocumentEditAndView { for(auto &document_change : workspace_edit.document_changes) {
LanguageProtocol::TextDocumentEdit *document_edit; if(auto edit = boost::get<TextDocumentEdit>(&document_change)) {
Source::View *view;
};
std::vector<DocumentEditAndView> document_edits_and_views;
for(auto &document_edit : workspace_edit.document_edits) {
Source::View *view = nullptr; Source::View *view = nullptr;
for(auto it = Source::View::views.begin(); it != Source::View::views.end(); ++it) { for(auto &e : Source::View::views) {
if((*it)->file_path == document_edit.file) { if(e->file_path == edit->file) {
view = *it; view = e;
break; break;
} }
} }
if(!view) { if(!view) {
if(!Notebook::get().open(document_edit.file)) { if(!Notebook::get().open(edit->file)) {
applied = false; applied = false;
return; return;
} }
view = Notebook::get().get_current_view(); view = Notebook::get().get_current_view();
document_edits_and_views.emplace_back(DocumentEditAndView{&document_edit, view});
}
else
document_edits_and_views.emplace_back(DocumentEditAndView{&document_edit, view});
} }
if(current_view)
Notebook::get().open(current_view);
for(auto &document_edit_and_view : document_edits_and_views) {
auto document_edit = document_edit_and_view.document_edit;
auto view = document_edit_and_view.view;
auto buffer = view->get_buffer(); auto buffer = view->get_buffer();
buffer->begin_user_action(); buffer->begin_user_action();
auto end_iter = buffer->end(); auto end_iter = buffer->end();
// If entire buffer is replaced // If entire buffer is replaced
if(document_edit->text_edits.size() == 1 && if(edit->text_edits.size() == 1 &&
document_edit->text_edits[0].range.start.line == 0 && document_edit->text_edits[0].range.start.character == 0 && edit->text_edits[0].range.start.line == 0 && edit->text_edits[0].range.start.character == 0 &&
(document_edit->text_edits[0].range.end.line > end_iter.get_line() || (edit->text_edits[0].range.end.line > end_iter.get_line() ||
(document_edit->text_edits[0].range.end.line == end_iter.get_line() && document_edit->text_edits[0].range.end.character >= current_view->get_line_pos(end_iter)))) { (edit->text_edits[0].range.end.line == end_iter.get_line() && edit->text_edits[0].range.end.character >= current_view->get_line_pos(end_iter)))) {
view->replace_text(document_edit->text_edits[0].new_text); view->replace_text(edit->text_edits[0].new_text);
} }
else { else {
for(auto text_edit_it = document_edit->text_edits.rbegin(); text_edit_it != document_edit->text_edits.rend(); ++text_edit_it) { for(auto text_edit_it = edit->text_edits.rbegin(); text_edit_it != edit->text_edits.rend(); ++text_edit_it) {
auto start_iter = view->get_iter_at_line_pos(text_edit_it->range.start.line, text_edit_it->range.start.character); auto start_iter = view->get_iter_at_line_pos(text_edit_it->range.start.line, text_edit_it->range.start.character);
auto end_iter = view->get_iter_at_line_pos(text_edit_it->range.end.line, text_edit_it->range.end.character); auto end_iter = view->get_iter_at_line_pos(text_edit_it->range.end.line, text_edit_it->range.end.character);
if(view != current_view) if(view != current_view)
@ -606,6 +602,10 @@ void LanguageProtocol::Client::handle_server_request(size_t id, const std::strin
} }
} }
} }
if(current_view)
Notebook::get().open(current_view);
}
}); });
result_processed.get_future().get(); result_processed.get_future().get();
write_response(id, std::string("{\"applied\":") + (applied ? "true" : "false") + '}'); write_response(id, std::string("{\"applied\":") + (applied ? "true" : "false") + '}');
@ -1007,19 +1007,19 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() {
++c; ++c;
usages.emplace_back(Offset(location.range.start.line, location.range.start.character, location.file), std::string()); usages.emplace_back(Offset(location.range.start.line, location.range.start.character, location.file), std::string());
auto &usage = usages.back(); auto &usage = usages.back();
auto view_it = views.end(); Source::View *view = nullptr;
for(auto it = views.begin(); it != views.end(); ++it) { for(auto &e : views) {
if(location.file == (*it)->file_path) { if(e->file_path == location.file) {
view_it = it; view = e;
break; break;
} }
} }
if(view_it != views.end()) { if(view) {
if(location.range.start.line < (*view_it)->get_buffer()->get_line_count()) { if(location.range.start.line < view->get_buffer()->get_line_count()) {
auto start = (*view_it)->get_buffer()->get_iter_at_line(location.range.start.line); auto start = view->get_buffer()->get_iter_at_line(location.range.start.line);
auto end = start; auto end = start;
end.forward_to_line_end(); end.forward_to_line_end();
usage.second = Glib::Markup::escape_text((*view_it)->get_buffer()->get_text(start, end)); usage.second = Glib::Markup::escape_text(view->get_buffer()->get_text(start, end));
embolden_token(usage.second, location.range.start.character, location.range.end.character); embolden_token(usage.second, location.range.start.character, location.range.end.character);
} }
} }
@ -1089,10 +1089,10 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() {
std::vector<LanguageProtocol::TextEdit> edits; std::vector<LanguageProtocol::TextEdit> edits;
for(auto &edit : result.array_or_empty()) for(auto &edit : result.array_or_empty())
edits.emplace_back(edit, text); edits.emplace_back(edit, text);
workspace_edit.document_edits.emplace_back(file_path.string(), std::move(edits)); workspace_edit.document_changes.emplace_back(LanguageProtocol::TextDocumentEdit(file_path.string(), std::move(edits)));
} }
catch(...) { catch(...) {
workspace_edit.document_edits.clear(); workspace_edit.document_changes.clear();
} }
} }
result_processed.set_value(); result_processed.set_value();
@ -1100,57 +1100,46 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() {
} }
result_processed.get_future().get(); result_processed.get_future().get();
if(workspace_edit.document_changes.empty())
return;
Terminal::get().print("Renamed ");
Terminal::get().print(previous_text, true);
Terminal::get().print(" to ");
Terminal::get().print(text, true);
Terminal::get().print(" at:\n");
auto current_view = Notebook::get().get_current_view(); auto current_view = Notebook::get().get_current_view();
struct DocumentEditAndView { std::set<Source::View *> views_to_be_closed;
LanguageProtocol::TextDocumentEdit *document_edit;
Source::View *view;
bool close;
};
std::vector<DocumentEditAndView> document_edits_and_views;
for(auto &document_edit : workspace_edit.document_edits) { bool update_directories = false;
for(auto &document_change : workspace_edit.document_changes) {
if(auto edit = boost::get<LanguageProtocol::TextDocumentEdit>(&document_change)) {
Source::View *view = nullptr; Source::View *view = nullptr;
for(auto it = views.begin(); it != views.end(); ++it) { for(auto &e : views) {
if((*it)->file_path == document_edit.file) { if(e->file_path == edit->file) {
view = *it; view = e;
break; break;
} }
} }
if(!view) { if(!view) {
if(!Notebook::get().open(document_edit.file)) if(!Notebook::get().open(edit->file))
return; return;
view = Notebook::get().get_current_view(); view = Notebook::get().get_current_view();
document_edits_and_views.emplace_back(DocumentEditAndView{&document_edit, view, true}); views_to_be_closed.emplace(view);
}
else
document_edits_and_views.emplace_back(DocumentEditAndView{&document_edit, view, false});
}
if(current_view)
Notebook::get().open(current_view);
if(!document_edits_and_views.empty()) {
Terminal::get().print("Renamed ");
Terminal::get().print(previous_text, true);
Terminal::get().print(" to ");
Terminal::get().print(text, true);
Terminal::get().print(" at:\n");
} }
for(auto &document_edit_and_view : document_edits_and_views) {
auto document_edit = document_edit_and_view.document_edit;
auto view = document_edit_and_view.view;
auto buffer = view->get_buffer(); auto buffer = view->get_buffer();
buffer->begin_user_action(); buffer->begin_user_action();
auto end_iter = buffer->end(); auto end_iter = buffer->end();
// If entire buffer is replaced // If entire buffer is replaced
if(document_edit->text_edits.size() == 1 && if(edit->text_edits.size() == 1 &&
document_edit->text_edits[0].range.start.line == 0 && document_edit->text_edits[0].range.start.character == 0 && edit->text_edits[0].range.start.line == 0 && edit->text_edits[0].range.start.character == 0 &&
(document_edit->text_edits[0].range.end.line > end_iter.get_line() || (edit->text_edits[0].range.end.line > end_iter.get_line() ||
(document_edit->text_edits[0].range.end.line == end_iter.get_line() && document_edit->text_edits[0].range.end.character >= get_line_pos(end_iter)))) { (edit->text_edits[0].range.end.line == end_iter.get_line() && edit->text_edits[0].range.end.character >= get_line_pos(end_iter)))) {
view->replace_text(document_edit->text_edits[0].new_text); view->replace_text(edit->text_edits[0].new_text);
Terminal::get().print(filesystem::get_short_path(view->file_path).string() + ":1:1\n"); Terminal::get().print(filesystem::get_short_path(view->file_path).string() + ":1:1\n");
} }
@ -1162,7 +1151,7 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() {
}; };
std::list<TerminalOutput> terminal_output_list; std::list<TerminalOutput> terminal_output_list;
for(auto text_edit_it = document_edit->text_edits.rbegin(); text_edit_it != document_edit->text_edits.rend(); ++text_edit_it) { for(auto text_edit_it = edit->text_edits.rbegin(); text_edit_it != edit->text_edits.rend(); ++text_edit_it) {
auto start_iter = view->get_iter_at_line_pos(text_edit_it->range.start.line, text_edit_it->range.start.character); auto start_iter = view->get_iter_at_line_pos(text_edit_it->range.start.line, text_edit_it->range.start.character);
auto end_iter = view->get_iter_at_line_pos(text_edit_it->range.end.line, text_edit_it->range.end.character); auto end_iter = view->get_iter_at_line_pos(text_edit_it->range.end.line, text_edit_it->range.end.character);
if(view != current_view) if(view != current_view)
@ -1194,12 +1183,41 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() {
if(!view->save()) if(!view->save())
return; return;
} }
else if(auto rename_file = boost::get<LanguageProtocol::RenameFile>(&document_change)) {
for(auto &document_edit_and_view : document_edits_and_views) { Source::View *view = nullptr;
if(document_edit_and_view.close) for(auto &e : views) {
Notebook::get().close(document_edit_and_view.view); if(e->file_path == rename_file->old_path) {
document_edit_and_view.close = false; view = e;
break;
}
}
if(!view) {
if(!Notebook::get().open(rename_file->new_path))
return;
}
boost::system::error_code ec;
boost::filesystem::rename(rename_file->old_path, rename_file->new_path, ec);
if(ec) {
Terminal::get().print("\e[31mError\e[m: could not rename file: " + ec.message() + '\n', true);
return;
}
if(view)
view->rename(rename_file->new_path);
else {
Notebook::get().get_current_view()->rename(rename_file->new_path);
Notebook::get().close_current();
}
update_directories = true;
} }
}
if(update_directories)
Directories::get().update();
if(current_view)
Notebook::get().open(current_view);
for(auto &view : views_to_be_closed)
Notebook::get().close(view);
}; };
} }
@ -1325,49 +1343,34 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() {
auto current_view = Notebook::get().get_current_view(); auto current_view = Notebook::get().get_current_view();
struct DocumentEditAndView { for(auto &document_change : workspace_edit.document_changes) {
LanguageProtocol::TextDocumentEdit *document_edit; if(auto edit = boost::get<LanguageProtocol::TextDocumentEdit>(&document_change)) {
Source::View *view;
};
std::vector<DocumentEditAndView> document_edits_and_views;
for(auto &document_edit : workspace_edit.document_edits) {
Source::View *view = nullptr; Source::View *view = nullptr;
for(auto it = views.begin(); it != views.end(); ++it) { for(auto &e : views) {
if((*it)->file_path == document_edit.file) { if(e->file_path == edit->file) {
view = *it; view = e;
break; break;
} }
} }
if(!view) { if(!view) {
if(!Notebook::get().open(document_edit.file)) if(!Notebook::get().open(edit->file))
return; return;
view = Notebook::get().get_current_view(); view = Notebook::get().get_current_view();
document_edits_and_views.emplace_back(DocumentEditAndView{&document_edit, view});
}
else
document_edits_and_views.emplace_back(DocumentEditAndView{&document_edit, view});
} }
if(current_view)
Notebook::get().open(current_view);
for(auto &document_edit_and_view : document_edits_and_views) {
auto document_edit = document_edit_and_view.document_edit;
auto view = document_edit_and_view.view;
auto buffer = view->get_buffer(); auto buffer = view->get_buffer();
buffer->begin_user_action(); buffer->begin_user_action();
auto end_iter = buffer->end(); auto end_iter = buffer->end();
// If entire buffer is replaced // If entire buffer is replaced
if(document_edit->text_edits.size() == 1 && if(edit->text_edits.size() == 1 &&
document_edit->text_edits[0].range.start.line == 0 && document_edit->text_edits[0].range.start.character == 0 && edit->text_edits[0].range.start.line == 0 && edit->text_edits[0].range.start.character == 0 &&
(document_edit->text_edits[0].range.end.line > end_iter.get_line() || (edit->text_edits[0].range.end.line > end_iter.get_line() ||
(document_edit->text_edits[0].range.end.line == end_iter.get_line() && document_edit->text_edits[0].range.end.character >= get_line_pos(end_iter)))) { (edit->text_edits[0].range.end.line == end_iter.get_line() && edit->text_edits[0].range.end.character >= get_line_pos(end_iter)))) {
view->replace_text(document_edit->text_edits[0].new_text); view->replace_text(edit->text_edits[0].new_text);
} }
else { else {
for(auto text_edit_it = document_edit->text_edits.rbegin(); text_edit_it != document_edit->text_edits.rend(); ++text_edit_it) { for(auto text_edit_it = edit->text_edits.rbegin(); text_edit_it != edit->text_edits.rend(); ++text_edit_it) {
auto start_iter = view->get_iter_at_line_pos(text_edit_it->range.start.line, text_edit_it->range.start.character); auto start_iter = view->get_iter_at_line_pos(text_edit_it->range.start.line, text_edit_it->range.start.character);
auto end_iter = view->get_iter_at_line_pos(text_edit_it->range.end.line, text_edit_it->range.end.character); auto end_iter = view->get_iter_at_line_pos(text_edit_it->range.end.line, text_edit_it->range.end.character);
if(view != current_view) if(view != current_view)
@ -1384,6 +1387,10 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() {
} }
} }
if(current_view)
Notebook::get().open(current_view);
}
if(capabilities.execute_command) { if(capabilities.execute_command) {
auto command = results[index].second->object_optional("command"); auto command = results[index].second->object_optional("command");
if(command) { if(command) {
@ -1939,8 +1946,9 @@ void Source::LanguageProtocolView::update_diagnostics_async(std::vector<Language
} }
if(edit) { if(edit) {
LanguageProtocol::WorkspaceEdit workspace_edit(*edit, file_path); LanguageProtocol::WorkspaceEdit workspace_edit(*edit, file_path);
for(auto &document_edit : workspace_edit.document_edits) { for(auto &document_change : workspace_edit.document_changes) {
for(auto &text_edit : document_edit.text_edits) { if(auto edit = boost::get<LanguageProtocol::TextDocumentEdit>(&document_change)) {
for(auto &text_edit : edit->text_edits) {
if(!quickfix_diagnostics.empty()) { if(!quickfix_diagnostics.empty()) {
for(auto &diagnostic : diagnostics) { for(auto &diagnostic : diagnostics) {
for(auto &quickfix_diagnostic : quickfix_diagnostics) { for(auto &quickfix_diagnostic : quickfix_diagnostics) {
@ -1948,7 +1956,7 @@ void Source::LanguageProtocolView::update_diagnostics_async(std::vector<Language
auto pair = diagnostic.quickfixes.emplace(title, std::set<Source::FixIt>{}); auto pair = diagnostic.quickfixes.emplace(title, std::set<Source::FixIt>{});
pair.first->second.emplace( pair.first->second.emplace(
text_edit.new_text, text_edit.new_text,
document_edit.file, edit->file,
std::make_pair<Offset, Offset>(Offset(text_edit.range.start.line, text_edit.range.start.character), std::make_pair<Offset, Offset>(Offset(text_edit.range.start.line, text_edit.range.start.character),
Offset(text_edit.range.end.line, text_edit.range.end.character))); Offset(text_edit.range.end.line, text_edit.range.end.character)));
break; break;
@ -1962,7 +1970,7 @@ void Source::LanguageProtocolView::update_diagnostics_async(std::vector<Language
auto pair = diagnostic.quickfixes.emplace(title, std::set<Source::FixIt>{}); auto pair = diagnostic.quickfixes.emplace(title, std::set<Source::FixIt>{});
pair.first->second.emplace( pair.first->second.emplace(
text_edit.new_text, text_edit.new_text,
document_edit.file, edit->file,
std::make_pair<Offset, Offset>(Offset(text_edit.range.start.line, text_edit.range.start.character), std::make_pair<Offset, Offset>(Offset(text_edit.range.start.line, text_edit.range.start.character),
Offset(text_edit.range.end.line, text_edit.range.end.character))); Offset(text_edit.range.end.line, text_edit.range.end.character)));
break; break;
@ -1975,6 +1983,7 @@ void Source::LanguageProtocolView::update_diagnostics_async(std::vector<Language
} }
} }
} }
}
catch(...) { catch(...) {
} }
} }

7
src/source_language_protocol.hpp

@ -102,11 +102,16 @@ namespace LanguageProtocol {
std::vector<TextEdit> text_edits; std::vector<TextEdit> text_edits;
}; };
struct RenameFile {
boost::filesystem::path old_path;
boost::filesystem::path new_path;
};
class WorkspaceEdit { class WorkspaceEdit {
public: public:
WorkspaceEdit() = default; WorkspaceEdit() = default;
WorkspaceEdit(const JSON &workspace_edit, boost::filesystem::path file_path); WorkspaceEdit(const JSON &workspace_edit, boost::filesystem::path file_path);
std::vector<TextDocumentEdit> document_edits; std::vector<boost::variant<TextDocumentEdit, RenameFile>> document_changes;
}; };
class Capabilities { class Capabilities {

2
tests/stubs/directories.cpp

@ -9,3 +9,5 @@ void Directories::on_save_file(const boost::filesystem::path &file_path) {}
bool Directories::on_button_press_event(GdkEventButton *event) { bool Directories::on_button_press_event(GdkEventButton *event) {
return false; return false;
}; };
void Directories::update() {}

2
tests/stubs/notebook.cpp

@ -13,3 +13,5 @@ bool Notebook::open(Source::View *view) { return true; }
void Notebook::open_uri(const std::string &uri) {} void Notebook::open_uri(const std::string &uri) {}
bool Notebook::close(Source::View *view) { return true; } bool Notebook::close(Source::View *view) { return true; }
bool Notebook::close_current() { return true; }

Loading…
Cancel
Save