diff --git a/src/filesystem.cc b/src/filesystem.cc index fdebe10..c01c6e8 100644 --- a/src/filesystem.cc +++ b/src/filesystem.cc @@ -269,3 +269,45 @@ boost::filesystem::path filesystem::find_executable(const std::string &executabl } return boost::filesystem::path(); } + +std::string filesystem::get_uri_from_path(const boost::filesystem::path &path) { + std::string uri{"file://"}; + + static auto hex_chars = "0123456789ABCDEF"; + + for(auto &chr : path.string()) { + static std::string encode_exceptions{"-._~!$&'()*+,;=:@?/\\"}; + if(!((chr >= '0' && chr <= '9') || (chr >= 'A' && chr <= 'Z') || (chr >= 'a' && chr <= 'z') || + std::any_of(encode_exceptions.begin(), encode_exceptions.end(), [chr](char e) { return chr == e; }))) + uri += std::string("%") + hex_chars[static_cast(chr) >> 4] + hex_chars[static_cast(chr) & 15]; + else + uri += chr; + } + + return uri; +} + +boost::filesystem::path filesystem::get_path_from_uri(const std::string &uri) { + std::string encoded; + + if(uri.compare(0, 7, "file://") == 0) + encoded = uri.substr(7); + else + encoded = uri; + + std::string unencoded; + for(size_t i = 0; i < encoded.size(); ++i) { + if(encoded[i] == '%' && i + 2 < encoded.size()) { + auto hex = encoded.substr(i + 1, 2); + auto decoded_chr = static_cast(std::strtol(hex.c_str(), nullptr, 16)); + unencoded += decoded_chr; + i += 2; + } + else if(encoded[i] == '+') + unencoded += ' '; + else + unencoded += encoded[i]; + } + + return unencoded; +} diff --git a/src/filesystem.h b/src/filesystem.h index 222475f..1860015 100644 --- a/src/filesystem.h +++ b/src/filesystem.h @@ -39,4 +39,9 @@ public: static const std::vector &get_executable_search_paths(); static boost::filesystem::path find_executable(const std::string &executable_name); + + /// Get uri from path + static std::string get_uri_from_path(const boost::filesystem::path &path); + /// Get path from file uri + static boost::filesystem::path get_path_from_uri(const std::string &uri); }; diff --git a/src/juci.cc b/src/juci.cc index ba2cf6d..15f9ba7 100644 --- a/src/juci.cc +++ b/src/juci.cc @@ -1,6 +1,7 @@ #include "juci.h" #include "config.h" #include "directories.h" +#include "filesystem.h" #include "menu.h" #include "notebook.h" #include "terminal.h" @@ -65,15 +66,16 @@ void Application::on_activate() { std::string files_in_directory; for(auto it = files.begin(); it != files.end();) { if(it->first.generic_string().compare(0, directory.generic_string().size() + 1, directory.generic_string() + '/') == 0) { - files_in_directory += " " + it->first.string(); + files_in_directory += " " + filesystem::escape_argument(it->first.string()); it = files.erase(it); } else it++; } std::thread another_juci_app([directory, files_in_directory]() { - Terminal::get().async_print("Executing: juci " + directory.string() + files_in_directory + "\n"); - Terminal::get().process("juci " + directory.string() + files_in_directory, "", false); + auto command = "juci " + filesystem::escape_argument(directory.string()) + files_in_directory; + Terminal::get().async_print("Executing: " + command + "\n"); + Terminal::get().process(command, "", false); }); another_juci_app.detach(); } diff --git a/src/project.cc b/src/project.cc index b7f016f..5536e0e 100644 --- a/src/project.cc +++ b/src/project.cc @@ -749,7 +749,7 @@ void Project::LanguageProtocol::show_symbols() { else { std::vector rows; std::promise result_processed; - client->write_request(language_protocol_view, "textDocument/documentSymbol", R"("textDocument":{"uri":"file://)" + language_protocol_view->file_path.string() + "\"}", [&result_processed, &rows, locations](const boost::property_tree::ptree &result, bool error) { + client->write_request(language_protocol_view, "textDocument/documentSymbol", R"("textDocument":{"uri":")" + language_protocol_view->uri + "\"}", [&result_processed, &rows, locations](const boost::property_tree::ptree &result, bool error) { if(!error) { for(auto it = result.begin(); it != result.end(); ++it) { try { diff --git a/src/source.cc b/src/source.cc index 6c77347..f9c3c83 100644 --- a/src/source.cc +++ b/src/source.cc @@ -674,7 +674,7 @@ void Source::View::setup_format_style(bool is_generic_view) { } } - command += " --stdin-filepath " + this->file_path.string() + " --print-width 120 --config-precedence prefer-file"; + command += " --stdin-filepath " + filesystem::escape_argument(this->file_path.string()) + " --print-width 120 --config-precedence prefer-file"; if(get_buffer()->get_has_selection()) { // Cannot be used together with --cursor-offset Gtk::TextIter start, end; diff --git a/src/source_language_protocol.cc b/src/source_language_protocol.cc index 49d8307..98f1a3c 100644 --- a/src/source_language_protocol.cc +++ b/src/source_language_protocol.cc @@ -23,8 +23,7 @@ LanguageProtocol::Range::Range(const boost::property_tree::ptree &pt) : start(pt LanguageProtocol::Location::Location(const boost::property_tree::ptree &pt, std::string file_) : range(pt.get_child("range")) { if(file_.empty()) { - file = pt.get("uri"); - file.erase(0, 7); + file = filesystem::get_path_from_uri(pt.get("uri")).string(); } else file = std::move(file_); @@ -40,8 +39,8 @@ LanguageProtocol::Diagnostic::Diagnostic(const boost::property_tree::ptree &pt) LanguageProtocol::TextEdit::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_)) {} -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) { +LanguageProtocol::Client::Client(boost::filesystem::path root_path_, std::string language_id_) : root_path(std::move(root_path_)), language_id(std::move(language_id_)) { + process = std::make_unique(filesystem::escape_argument(language_id + "-language-server"), root_path.string(), [this](const char *bytes, size_t n) { server_message_stream.write(bytes, n); parse_server_message(); }, [](const char *bytes, size_t n) { @@ -50,14 +49,14 @@ LanguageProtocol::Client::Client(std::string root_uri_, std::string language_id_ } std::shared_ptr LanguageProtocol::Client::get(const boost::filesystem::path &file_path, const std::string &language_id) { - std::string root_uri; + boost::filesystem::path root_path; auto build = Project::Build::create(file_path); if(!build->project_path.empty()) - root_uri = build->project_path.string(); + root_path = build->project_path; else - root_uri = file_path.parent_path().string(); + root_path = file_path.parent_path(); - auto cache_id = root_uri + '|' + language_id; + auto cache_id = root_path.string() + '|' + language_id; static std::unordered_map> cache; static std::mutex mutex; @@ -67,7 +66,7 @@ std::shared_ptr LanguageProtocol::Client::get(const bo it = cache.emplace(cache_id, std::weak_ptr()).first; auto instance = it->second.lock(); if(!instance) - it->second = instance = std::shared_ptr(new Client(root_uri, language_id), [](Client *client_ptr) { + it->second = instance = std::shared_ptr(new Client(root_path, language_id), [](Client *client_ptr) { std::thread delete_thread([client_ptr] { delete client_ptr; }); @@ -113,7 +112,7 @@ LanguageProtocol::Capabilities LanguageProtocol::Client::initialize(Source::Lang return capabilities; std::promise result_processed; - write_request(nullptr, "initialize", "\"processId\":" + std::to_string(process->get_id()) + R"(,"rootUri":"file://)" + root_uri + R"(","capabilities":{"workspace":{"didChangeConfiguration":{"dynamicRegistration":true},"didChangeWatchedFiles":{"dynamicRegistration":true},"symbol":{"dynamicRegistration":true},"executeCommand":{"dynamicRegistration":true}},"textDocument":{"synchronization":{"dynamicRegistration":true,"willSave":true,"willSaveWaitUntil":true,"didSave":true},"completion":{"dynamicRegistration":true,"completionItem":{"snippetSupport":true}},"hover":{"dynamicRegistration":true},"signatureHelp":{"dynamicRegistration":true},"definition":{"dynamicRegistration":true},"references":{"dynamicRegistration":true},"documentHighlight":{"dynamicRegistration":true},"documentSymbol":{"dynamicRegistration":true},"codeAction":{"dynamicRegistration":true},"codeLens":{"dynamicRegistration":true},"formatting":{"dynamicRegistration":true},"rangeFormatting":{"dynamicRegistration":true},"onTypeFormatting":{"dynamicRegistration":true},"rename":{"dynamicRegistration":true},"documentLink":{"dynamicRegistration":true}}},"initializationOptions":{"omitInitBuild":true},"trace":"off")", [this, &result_processed](const boost::property_tree::ptree &result, bool error) { + write_request(nullptr, "initialize", "\"processId\":" + std::to_string(process->get_id()) + R"(,"rootUri":")" + filesystem::get_uri_from_path(root_path) + R"(","capabilities":{"workspace":{"didChangeConfiguration":{"dynamicRegistration":true},"didChangeWatchedFiles":{"dynamicRegistration":true},"symbol":{"dynamicRegistration":true},"executeCommand":{"dynamicRegistration":true}},"textDocument":{"synchronization":{"dynamicRegistration":true,"willSave":true,"willSaveWaitUntil":true,"didSave":true},"completion":{"dynamicRegistration":true,"completionItem":{"snippetSupport":true}},"hover":{"dynamicRegistration":true},"signatureHelp":{"dynamicRegistration":true},"definition":{"dynamicRegistration":true},"references":{"dynamicRegistration":true},"documentHighlight":{"dynamicRegistration":true},"documentSymbol":{"dynamicRegistration":true},"codeAction":{"dynamicRegistration":true},"codeLens":{"dynamicRegistration":true},"formatting":{"dynamicRegistration":true},"rangeFormatting":{"dynamicRegistration":true},"onTypeFormatting":{"dynamicRegistration":true},"rename":{"dynamicRegistration":true},"documentLink":{"dynamicRegistration":true}}},"initializationOptions":{"omitInitBuild":true},"trace":"off")", [this, &result_processed](const boost::property_tree::ptree &result, bool error) { if(!error) { auto capabilities_pt = result.find("capabilities"); if(capabilities_pt != result.not_found()) { @@ -322,8 +321,8 @@ void LanguageProtocol::Client::write_notification(const std::string &method, con void LanguageProtocol::Client::handle_server_request(const std::string &method, const boost::property_tree::ptree ¶ms) { if(method == "textDocument/publishDiagnostics") { std::vector diagnostics; - auto uri = params.get("uri", ""); - if(!uri.empty()) { + auto file = filesystem::get_path_from_uri(params.get("uri", "")); + if(!file.empty()) { auto diagnostics_pt = params.get_child("diagnostics", boost::property_tree::ptree()); for(auto it = diagnostics_pt.begin(); it != diagnostics_pt.end(); ++it) { try { @@ -334,7 +333,7 @@ void LanguageProtocol::Client::handle_server_request(const std::string &method, } std::lock_guard lock(views_mutex); for(auto view : views) { - if(uri.size() >= 7 && uri.compare(7, std::string::npos, view->file_path.string()) == 0) { + if(file == view->file_path) { view->update_diagnostics(std::move(diagnostics)); break; } @@ -344,7 +343,7 @@ 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), 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), uri(filesystem::get_uri_from_path(file_path)), 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); @@ -380,7 +379,7 @@ Source::LanguageProtocolView::LanguageProtocolView(const boost::filesystem::path escape_text(text); content_changes = R"({"text":")" + text + "\"}"; } - client->write_notification("textDocument/didChange", R"("textDocument":{"uri":"file://)" + this->file_path.string() + R"(","version":)" + std::to_string(document_version++) + "},\"contentChanges\":[" + content_changes + "]"); + client->write_notification("textDocument/didChange", R"("textDocument":{"uri":")" + this->uri + 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) { @@ -394,7 +393,7 @@ Source::LanguageProtocolView::LanguageProtocolView(const boost::filesystem::path escape_text(text); content_changes = R"({"text":")" + text + "\"}"; } - client->write_notification("textDocument/didChange", R"("textDocument":{"uri":"file://)" + this->file_path.string() + R"(","version":)" + std::to_string(document_version++) + "},\"contentChanges\":[" + content_changes + "]"); + client->write_notification("textDocument/didChange", R"("textDocument":{"uri":")" + this->uri + R"(","version":)" + std::to_string(document_version++) + "},\"contentChanges\":[" + content_changes + "]"); }, false); } @@ -418,7 +417,7 @@ void Source::LanguageProtocolView::initialize(bool setup) { 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 + "\"}"); + client->write_notification("textDocument/didOpen", R"("textDocument":{"uri":")" + uri + R"(","languageId":")" + language_id + R"(","version":)" + std::to_string(document_version++) + R"(,"text":")" + text + "\"}"); if(setup) { setup_autocomplete(); @@ -448,7 +447,7 @@ void Source::LanguageProtocolView::close() { if(autocomplete.thread.joinable()) autocomplete.thread.join(); - client->write_notification("textDocument/didClose", R"("textDocument":{"uri":"file://)" + file_path.string() + "\"}"); + client->write_notification("textDocument/didClose", R"("textDocument":{"uri":")" + uri + "\"}"); client->close(this); client = nullptr; } @@ -460,6 +459,7 @@ Source::LanguageProtocolView::~LanguageProtocolView() { void Source::LanguageProtocolView::rename(const boost::filesystem::path &path) { close(); Source::DiffView::rename(path); + uri = filesystem::get_uri_from_path(path); client = LanguageProtocol::Client::get(file_path, language_id); initialize(false); } @@ -517,11 +517,11 @@ 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":"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 + "}"; + 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 + "}"; } else { method = "textDocument/formatting"; - params = R"("textDocument":{"uri":"file://)" + file_path.string() + R"("},"options":{)" + options + "}"; + params = R"("textDocument":{"uri":")" + uri + R"("},"options":{)" + options + "}"; } client->write_request(this, method, params, [&text_edits, &result_processed](const boost::property_tree::ptree &result, bool error) { @@ -588,7 +588,7 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() { else method = "textDocument/documentHighlight"; - 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) { + 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, &locations, &result_processed](const boost::property_tree::ptree &result, bool error) { if(!error) { try { for(auto it = result.begin(); it != result.end(); ++it) @@ -724,7 +724,7 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() { std::vector changes; std::promise result_processed; if(capabilities.rename) { - 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) { + 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, &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); @@ -751,14 +751,13 @@ 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 file = document_it->second.get("uri", ""); - file.erase(0, 7); + auto file = filesystem::get_path_from_uri(document_it->second.get("uri", "")); if(filesystem::file_in_path(file, 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(file), std::move(edits)}); + changes.emplace_back(Changes{file.string(), std::move(edits)}); } } } @@ -772,7 +771,7 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() { }); } else { - 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) { + 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, &changes, &text, &result_processed](const boost::property_tree::ptree &result, bool error) { if(!error) { try { std::vector edits; @@ -875,7 +874,7 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() { std::vector> methods; std::promise result_processed; - client->write_request(this, "textDocument/documentSymbol", R"("textDocument":{"uri":"file://)" + file_path.string() + "\"}", [&result_processed, &methods](const boost::property_tree::ptree &result, bool error) { + client->write_request(this, "textDocument/documentSymbol", R"("textDocument":{"uri":")" + uri + "\"}", [&result_processed, &methods](const boost::property_tree::ptree &result, bool error) { if(!error) { for(auto it = result.begin(); it != result.end(); ++it) { try { @@ -1022,7 +1021,7 @@ void Source::LanguageProtocolView::show_type_tooltips(const Gdk::Rectangle &rect static int request_count = 0; request_count++; auto current_request = request_count; - client->write_request(this, "textDocument/hover", R"("textDocument": {"uri":"file://)" + file_path.string() + R"("}, "position": {"line": )" + std::to_string(iter.get_line()) + ", \"character\": " + std::to_string(iter.get_line_offset()) + "}", [this, offset, current_request](const boost::property_tree::ptree &result, bool error) { + client->write_request(this, "textDocument/hover", R"("textDocument": {"uri":")" + uri + R"("}, "position": {"line": )" + std::to_string(iter.get_line()) + ", \"character\": " + std::to_string(iter.get_line_offset()) + "}", [this, offset, current_request](const boost::property_tree::ptree &result, bool error) { if(!error) { // hover result structure vary significantly from the different language servers auto content = std::make_shared(); @@ -1124,12 +1123,9 @@ void Source::LanguageProtocolView::apply_similar_symbol_tag() { static int request_count = 0; request_count++; auto current_request = request_count; - 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) { + 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) { if(!error) { std::vector ranges; - std::string uri; - if(!capabilities.document_highlight) - uri = "file://" + file_path.string(); for(auto it = result.begin(); it != result.end(); ++it) { try { if(capabilities.document_highlight || it->second.get("uri") == uri) @@ -1158,7 +1154,7 @@ void Source::LanguageProtocolView::apply_clickable_tag(const Gtk::TextIter &iter auto current_request = request_count; auto line = iter.get_line(); auto offset = iter.get_line_offset(); - client->write_request(this, "textDocument/definition", R"("textDocument":{"uri":"file://)" + file_path.string() + R"("}, "position": {"line": )" + std::to_string(line) + ", \"character\": " + std::to_string(offset) + "}", [this, current_request, line, offset](const boost::property_tree::ptree &result, bool error) { + client->write_request(this, "textDocument/definition", R"("textDocument":{"uri":")" + uri + R"("}, "position": {"line": )" + std::to_string(line) + ", \"character\": " + std::to_string(offset) + "}", [this, current_request, line, offset](const boost::property_tree::ptree &result, bool error) { if(!error && !result.empty()) { dispatcher.post([this, current_request, line, offset] { if(current_request != request_count || !clickable_tag_applied) @@ -1174,7 +1170,7 @@ 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(); std::promise result_processed; - 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) { + 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) { if(!error) { for(auto it = result.begin(); it != result.end(); ++it) { try { @@ -1349,7 +1345,7 @@ void Source::LanguageProtocolView::setup_autocomplete() { if(autocomplete_show_parameters) { if(!capabilities.signature_help) return; - client->write_request(this, "textDocument/signatureHelp", 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) { + client->write_request(this, "textDocument/signatureHelp", 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) { if(!error) { auto signatures = result.get_child("signatures", boost::property_tree::ptree()); for(auto signature_it = signatures.begin(); signature_it != signatures.end(); ++signature_it) { @@ -1368,7 +1364,7 @@ void Source::LanguageProtocolView::setup_autocomplete() { }); } else { - 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) { + 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) { if(!error) { auto begin = result.begin(); // rust language server is bugged auto end = result.end(); @@ -1502,7 +1498,7 @@ bool Source::LanguageProtocolView::has_named_parameters() { void Source::LanguageProtocolView::update_flow_coverage() { std::stringstream stdin_stream, stderr_stream; auto stdout_stream = std::make_shared(); - auto exit_status = Terminal::get().process(stdin_stream, *stdout_stream, flow_coverage_executable.string() + " coverage --json " + file_path.string(), "", &stderr_stream); + auto exit_status = Terminal::get().process(stdin_stream, *stdout_stream, flow_coverage_executable.string() + " coverage --json " + filesystem::escape_argument(file_path.string()), "", &stderr_stream); dispatcher.post([this, exit_status, stdout_stream] { if(!flow_coverage_cleared_diagnostic_tooltips) { diff --git a/src/source_language_protocol.h b/src/source_language_protocol.h index cdfc249..4e3a8b1 100644 --- a/src/source_language_protocol.h +++ b/src/source_language_protocol.h @@ -79,8 +79,8 @@ namespace LanguageProtocol { }; class Client { - Client(std::string root_uri, std::string language_id); - std::string root_uri; + Client(boost::filesystem::path root_path, std::string language_id); + boost::filesystem::path root_path; std::string language_id; Capabilities capabilities; @@ -135,6 +135,8 @@ namespace Source { Gtk::TextIter get_iter_at_line_pos(int line, int pos) override; + std::string uri; + protected: void show_type_tooltips(const Gdk::Rectangle &rectangle) override; void apply_similar_symbol_tag() override; diff --git a/tests/filesystem_test.cc b/tests/filesystem_test.cc index 57b732b..9f1c5ce 100644 --- a/tests/filesystem_test.cc +++ b/tests/filesystem_test.cc @@ -84,4 +84,11 @@ int main() { 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"); } + + { + boost::filesystem::path path = "/ro ot/te stæøå.txt"; + auto uri = filesystem::get_uri_from_path(path); + g_assert(uri == "file:///ro%20ot/te%20st%C3%A6%C3%B8%C3%A5.txt"); + g_assert(path == filesystem::get_path_from_uri(uri)); + } } \ No newline at end of file