From 95ecfab79e8a6c4d95cfb9352b3856866e9676bb Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Fri, 26 May 2023 17:21:00 +0200 Subject: [PATCH 1/5] common: lsp: lsp-file-utils: Added methods for encoding/decoding paths in URIs The URIs in LSP requests from editors are following RFC 3986 - this commit adds parsing escaped symbols in the paths, such as spaces, colons Signed-off-by: Grzegorz Latosinski --- common/lsp/lsp-file-utils.cc | 93 ++++++++++++++++++++++++++++++++++-- common/lsp/lsp-file-utils.h | 2 +- 2 files changed, 91 insertions(+), 4 deletions(-) diff --git a/common/lsp/lsp-file-utils.cc b/common/lsp/lsp-file-utils.cc index 70f911ae4..9fb741004 100644 --- a/common/lsp/lsp-file-utils.cc +++ b/common/lsp/lsp-file-utils.cc @@ -17,6 +17,7 @@ #include +#include "absl/strings/escaping.h" #include "absl/strings/match.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" @@ -25,14 +26,100 @@ namespace verible::lsp { static constexpr absl::string_view kFileSchemePrefix = "file://"; -absl::string_view LSPUriToPath(absl::string_view uri) { +namespace { + +bool NeedsEscape(char c) { + if (isalnum(c)) return false; + switch (c) { + case '-': + case '/': + case '.': + case '_': + case '~': + return false; + } + return true; +} + +std::string DecodeURI(absl::string_view uri) { + std::string result; + result.reserve(uri.size() - 2 * std::count(uri.begin(), uri.end(), '%')); + absl::string_view::size_type pos = 0; + + while (pos < uri.size()) { + if (uri[pos] == '%') { + pos++; + if (pos + 2 <= uri.size() && std::isxdigit(uri[pos]) && + std::isxdigit(uri[pos + 1])) { + std::string hex = absl::HexStringToBytes(uri.substr(pos, 2)); + absl::StrAppend(&result, hex.length() == 1 ? hex : uri.substr(pos, 2)); + pos += 2; + } else { + absl::StrAppend(&result, "%"); + } + } + absl::string_view::size_type nextpos = uri.find('%', pos); + if (nextpos > absl::string_view::npos) nextpos = uri.size(); + absl::StrAppend(&result, uri.substr(pos, nextpos - pos)); + pos = nextpos; + } + return result; +} + +std::string EncodeURI(absl::string_view uri) { + std::string result; + + absl::string_view::size_type pos = 0; + + int prevpos = 0; + while (pos < uri.size()) { + if (NeedsEscape(uri[pos])) { + absl::StrAppend(&result, uri.substr(prevpos, pos - prevpos)); + absl::StrAppend(&result, "%", absl::Hex(uri[pos], absl::kZeroPad2)); + prevpos = ++pos; + } else { + pos++; + } + } + absl::StrAppend(&result, uri.substr(prevpos, pos - prevpos)); + return result; +} +} // namespace + +std::string LSPUriToPath(absl::string_view uri) { if (!absl::StartsWith(uri, kFileSchemePrefix)) return ""; - return uri.substr(kFileSchemePrefix.size()); + std::string path = DecodeURI(uri.substr(kFileSchemePrefix.size())); + // In Windows, paths in URIs are represented as + // file:///c:/Users/user/project/file.sv + // Which results in paths: + // /c:/Users/user/project/file.sv + // The prefix "/" needs to be removed from the path +#ifdef _WIN32 + if (path.length() >= 3 && path[0] == '/' && isalpha(path[1]) && + path[2] == ':') { + path = path.substr(1); + } +#endif + return path; } std::string PathToLSPUri(absl::string_view path) { std::filesystem::path p(path.begin(), path.end()); - return absl::StrCat(kFileSchemePrefix, std::filesystem::absolute(p).string()); + std::string normalized_path; + normalized_path = std::filesystem::absolute(p).string(); +#ifdef _WIN32 + if (normalized_path.length() >= 2 && isalpha(normalized_path[0]) && + normalized_path[1] == ':') { + // In Windows, paths in URIs are represented as + // file:///c:/Users/user/project/file.sv + // Which results in paths: + // /c:/Users/user/project/file.sv + // The prefix "/" needs to be added + normalized_path = absl::StrCat("/", normalized_path); + } +#endif + normalized_path = EncodeURI(normalized_path); + return absl::StrCat(kFileSchemePrefix, normalized_path); } } // namespace verible::lsp diff --git a/common/lsp/lsp-file-utils.h b/common/lsp/lsp-file-utils.h index bd8bc0141..cc1dff742 100644 --- a/common/lsp/lsp-file-utils.h +++ b/common/lsp/lsp-file-utils.h @@ -23,7 +23,7 @@ namespace verible::lsp { // If other scheme is provided, method returns empty string_view. // TODO (glatosinski) current resolving of LSP URIs is very naive // and supports only narrow use cases of file:// specifier. -absl::string_view LSPUriToPath(absl::string_view uri); +std::string LSPUriToPath(absl::string_view uri); // Converts filesystem paths to file:// scheme entries. std::string PathToLSPUri(absl::string_view path); From e2b22bf17757659b9d4a7039d44923abf3df0bc8 Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Tue, 6 Jun 2023 17:12:06 +0200 Subject: [PATCH 2/5] verilog: tools: ls: Changed return value for LSPUriToPath to std::string Signed-off-by: Grzegorz Latosinski --- verilog/tools/ls/lsp-parse-buffer.cc | 2 +- verilog/tools/ls/symbol-table-handler.cc | 2 +- verilog/tools/ls/verilog-language-server.cc | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/verilog/tools/ls/lsp-parse-buffer.cc b/verilog/tools/ls/lsp-parse-buffer.cc index 3ecf794b3..ed9799f63 100644 --- a/verilog/tools/ls/lsp-parse-buffer.cc +++ b/verilog/tools/ls/lsp-parse-buffer.cc @@ -25,7 +25,7 @@ static absl::StatusOr> RunLinter( const auto &text_structure = parser.Data(); verilog::LinterConfiguration config; - const absl::string_view file_path = verible::lsp::LSPUriToPath(filename); + const std::string file_path = verible::lsp::LSPUriToPath(filename); if (auto from_flags = LinterConfigurationFromFlags(file_path); from_flags.ok()) { config = *from_flags; diff --git a/verilog/tools/ls/symbol-table-handler.cc b/verilog/tools/ls/symbol-table-handler.cc index 39d66b00c..7bb81f179 100644 --- a/verilog/tools/ls/symbol-table-handler.cc +++ b/verilog/tools/ls/symbol-table-handler.cc @@ -267,7 +267,7 @@ std::vector SymbolTableHandler::FindDefinitionLocation( const verilog::BufferTrackerContainer &parsed_buffers) { // TODO add iterating over multiple definitions Prepare(); - const absl::string_view filepath = LSPUriToPath(params.textDocument.uri); + const std::string filepath = LSPUriToPath(params.textDocument.uri); std::string relativepath = curr_project_->GetRelativePathToSource(filepath); absl::string_view symbol = GetTokenAtTextDocumentPosition(params, parsed_buffers); diff --git a/verilog/tools/ls/verilog-language-server.cc b/verilog/tools/ls/verilog-language-server.cc index f8aff85cd..0af2d0f08 100644 --- a/verilog/tools/ls/verilog-language-server.cc +++ b/verilog/tools/ls/verilog-language-server.cc @@ -176,7 +176,7 @@ verible::lsp::InitializeResult VerilogLanguageServer::InitializeRequestHandler( const verible::lsp::InitializeParams &p) { // set VerilogProject for the symbol table, if possible if (!p.rootUri.empty()) { - absl::string_view path = verible::lsp::LSPUriToPath(p.rootUri); + std::string path = verible::lsp::LSPUriToPath(p.rootUri); if (path.empty()) { LOG(ERROR) << "Unsupported rootUri in initialize request: " << p.rootUri << std::endl; @@ -232,7 +232,7 @@ void VerilogLanguageServer::SendDiagnostics( void VerilogLanguageServer::UpdateEditedFileInProject( const std::string &uri, const verilog::BufferTracker *buffer_tracker) { - const absl::string_view path = verible::lsp::LSPUriToPath(uri); + const std::string path = verible::lsp::LSPUriToPath(uri); if (path.empty()) { LOG(ERROR) << "Could not convert LS URI to path: " << uri; return; From fec6f2e3435943da03360bee170d3d747c8f9f36 Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Fri, 26 May 2023 17:23:05 +0200 Subject: [PATCH 3/5] common: lsp: lsp-file-utils: Added initial tests for conversion Signed-off-by: Grzegorz Latosinski --- common/lsp/BUILD | 10 ++++ common/lsp/lsp-file-utils_test.cc | 86 +++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 common/lsp/lsp-file-utils_test.cc diff --git a/common/lsp/BUILD b/common/lsp/BUILD index a1876d75a..062ebc52d 100644 --- a/common/lsp/BUILD +++ b/common/lsp/BUILD @@ -130,6 +130,16 @@ cc_library( ], ) +cc_test( + name = "lsp-file-utils_test", + srcs = ["lsp-file-utils_test.cc"], + deps = [ + ":lsp-file-utils", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + cc_binary( name = "json-rpc-expect", srcs = ["json-rpc-expect.cc"], diff --git a/common/lsp/lsp-file-utils_test.cc b/common/lsp/lsp-file-utils_test.cc new file mode 100644 index 000000000..5a6fe157f --- /dev/null +++ b/common/lsp/lsp-file-utils_test.cc @@ -0,0 +1,86 @@ +// Copyright 2023 The Verible Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "common/lsp/lsp-file-utils.h" + +#include "absl/strings/str_cat.h" +#include "gtest/gtest.h" + +namespace verible { +namespace lsp { + +std::string PathPrefix(const std::string &path) { +#ifdef _WIN32 + return absl::StrCat("y:/", path); +#else + return absl::StrCat("/", path); +#endif +} + +std::string URIPrefix(const std::string &path) { +#ifdef _WIN32 + return absl::StrCat("file:///y%3a/", path); +#else + return absl::StrCat("file:///", path); +#endif +} + +TEST(URIDecodingTest, SimplePathDecoding) { + ASSERT_EQ(PathPrefix("test-project/add.sv"), + LSPUriToPath(URIPrefix("test-project/add.sv"))); +} + +TEST(URIEncodingTest, SimplePathEncoding) { + ASSERT_EQ(URIPrefix("test-project/add.sv"), + PathToLSPUri(PathPrefix("test-project/add.sv"))); +} + +TEST(URIDecodingtest, PathWithSpacesDecoding) { + ASSERT_EQ(PathPrefix("test project/my module.sv"), + LSPUriToPath(URIPrefix("test%20project/my%20module.sv"))); +} + +TEST(URIEncodingTest, PathWithSpacesEncoding) { + ASSERT_EQ(URIPrefix("test%20project/my%20module.sv"), + PathToLSPUri(PathPrefix("test project/my module.sv"))); +} + +TEST(URIDecodingtest, PathWithConsecutiveEscapes) { + ASSERT_EQ(PathPrefix("test project/my module.sv"), + LSPUriToPath(URIPrefix("test%20project/my%20%20%20%20module.sv"))); +} + +TEST(URIEncodingTest, PathWithConsecutiveEscapes) { + ASSERT_EQ(URIPrefix("test%20project/my%20%20%20%20module.sv"), + PathToLSPUri(PathPrefix("test project/my module.sv"))); +} + +TEST(URIDecodingTest, InvalidHex) { + ASSERT_EQ(PathPrefix("test-project/my-module-%qz.sv"), + LSPUriToPath(URIPrefix("test-project/my-module-%qz.sv"))); +} + +TEST(URIDecodingTest, HexConversions) { + ASSERT_EQ(PathPrefix("test-project/my-module-%q"), + LSPUriToPath(URIPrefix("test-project/my-module-%q"))); + ASSERT_EQ(PathPrefix("test-project/my-module-%xyz"), + LSPUriToPath(URIPrefix("test-project/my-module-%xyz"))); + ASSERT_EQ(PathPrefix(" "), LSPUriToPath(URIPrefix("%20"))); + ASSERT_EQ(PathPrefix("%2"), LSPUriToPath(URIPrefix("%2"))); + ASSERT_EQ(PathPrefix("%"), LSPUriToPath(URIPrefix("%"))); +} + +} // namespace lsp +} // namespace verible From 277fa0f9edcae8afc46168e761ecce0e765a722b Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Wed, 14 Jun 2023 17:51:26 +0200 Subject: [PATCH 4/5] common: lsp: lsp-file-utils: Fix handling of backslashes Signed-off-by: Grzegorz Latosinski --- common/lsp/lsp-file-utils.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/common/lsp/lsp-file-utils.cc b/common/lsp/lsp-file-utils.cc index 9fb741004..4f02f9b62 100644 --- a/common/lsp/lsp-file-utils.cc +++ b/common/lsp/lsp-file-utils.cc @@ -15,6 +15,7 @@ #include "common/lsp/lsp-file-utils.h" +#include #include #include "absl/strings/escaping.h" @@ -36,6 +37,7 @@ bool NeedsEscape(char c) { case '.': case '_': case '~': + case '\\': return false; } return true; @@ -107,6 +109,9 @@ std::string PathToLSPUri(absl::string_view path) { std::filesystem::path p(path.begin(), path.end()); std::string normalized_path; normalized_path = std::filesystem::absolute(p).string(); + std::transform(normalized_path.cbegin(), normalized_path.cend(), + normalized_path.begin(), + [](char c) { return c == '\\' ? '/' : c; }); #ifdef _WIN32 if (normalized_path.length() >= 2 && isalpha(normalized_path[0]) && normalized_path[1] == ':') { From bb87dafdd26255b6b115414978b7c7981c19c3fc Mon Sep 17 00:00:00 2001 From: Grzegorz Latosinski Date: Thu, 15 Jun 2023 12:17:58 +0200 Subject: [PATCH 5/5] verilog: tools: ls: verilog-language-server: test: Added using PathToLSPUri instead of custom URI conversion Signed-off-by: Grzegorz Latosinski --- verilog/tools/ls/BUILD | 1 + .../tools/ls/verilog-language-server_test.cc | 227 ++++++++++-------- 2 files changed, 123 insertions(+), 105 deletions(-) diff --git a/verilog/tools/ls/BUILD b/verilog/tools/ls/BUILD index 32140f813..4691fc3a5 100644 --- a/verilog/tools/ls/BUILD +++ b/verilog/tools/ls/BUILD @@ -167,6 +167,7 @@ cc_test( ":verilog-language-server", "//common/lsp:lsp-protocol", "//common/lsp:lsp-protocol-enums", + "//common/lsp:lsp-file-utils", "//common/util:file-util", "//verilog/analysis:verilog-linter", "@com_google_absl//absl/flags:flag", diff --git a/verilog/tools/ls/verilog-language-server_test.cc b/verilog/tools/ls/verilog-language-server_test.cc index af8bbaeb4..39a77bd26 100644 --- a/verilog/tools/ls/verilog-language-server_test.cc +++ b/verilog/tools/ls/verilog-language-server_test.cc @@ -18,6 +18,8 @@ #include "absl/flags/flag.h" #include "absl/strings/match.h" +#include "absl/strings/str_replace.h" +#include "common/lsp/lsp-file-utils.h" #include "common/lsp/lsp-protocol-enums.h" #include "common/lsp/lsp-protocol.h" #include "common/util/file_util.h" @@ -37,6 +39,7 @@ namespace { // TODO (glatosinski) for JSON messages use types defined in lsp-protocol.h using nlohmann::json; +using verible::lsp::PathToLSPUri; // TODO (glatosinski) use better sample modules static constexpr absl::string_view // @@ -136,10 +139,11 @@ class VerilogLanguageServerTest : public ::testing::Test { class VerilogLanguageServerSymbolTableTest : public VerilogLanguageServerTest { public: absl::Status InitializeCommunication() override { - json initialize_request = {{"jsonrpc", "2.0"}, - {"id", 1}, - {"method", "initialize"}, - {"params", {{"rootUri", "file://" + root_dir}}}}; + json initialize_request = { + {"jsonrpc", "2.0"}, + {"id", 1}, + {"method", "initialize"}, + {"params", {{"rootUri", PathToLSPUri(root_dir)}}}}; return SendRequest(initialize_request.dump()); } @@ -631,16 +635,16 @@ TEST_F(VerilogLanguageServerSymbolTableTest, DefinitionRequestTest) { const verible::file::testing::ScopedTestFile module_a(root_dir, kSampleModuleA, "a.sv"); + const std::string module_a_uri = PathToLSPUri(module_a.filename()); const std::string module_a_open_request = - DidOpenRequest("file://" + module_a.filename(), kSampleModuleA); + DidOpenRequest(module_a_uri, kSampleModuleA); ASSERT_OK(SendRequest(module_a_open_request)); // obtain diagnostics GetResponse(); // find definition for "var1" variable in a.sv file - std::string definition_request = - DefinitionRequest("file://" + module_a.filename(), 2, 2, 16); + std::string definition_request = DefinitionRequest(module_a_uri, 2, 2, 16); ASSERT_OK(SendRequest(definition_request)); json response = json::parse(GetResponse()); @@ -651,7 +655,7 @@ TEST_F(VerilogLanguageServerSymbolTableTest, DefinitionRequestTest) { ASSERT_EQ(response["result"][0]["range"]["start"]["character"], 9); ASSERT_EQ(response["result"][0]["range"]["end"]["line"], 1); ASSERT_EQ(response["result"][0]["range"]["end"]["character"], 13); - ASSERT_EQ(response["result"][0]["uri"], "file://" + module_a.filename()); + ASSERT_EQ(response["result"][0]["uri"], module_a_uri); } // Check textDocument/definition request when there are two symbols of the same @@ -667,19 +671,20 @@ TEST_F(VerilogLanguageServerSymbolTableTest, const verible::file::testing::ScopedTestFile module_b(root_dir, kSampleModuleB, "b.sv"); + const std::string module_a_uri = PathToLSPUri(module_a.filename()); + const std::string module_b_uri = PathToLSPUri(module_b.filename()); const std::string module_a_open_request = - DidOpenRequest("file://" + module_a.filename(), kSampleModuleA); + DidOpenRequest(module_a_uri, kSampleModuleA); ASSERT_OK(SendRequest(module_a_open_request)); const std::string module_b_open_request = - DidOpenRequest("file://" + module_b.filename(), kSampleModuleB); + DidOpenRequest(module_b_uri, kSampleModuleB); ASSERT_OK(SendRequest(module_b_open_request)); // obtain diagnostics for both files GetResponse(); // find definition for "var1" variable in b.sv file - std::string definition_request = - DefinitionRequest("file://" + module_b.filename(), 2, 2, 16); + std::string definition_request = DefinitionRequest(module_b_uri, 2, 2, 16); ASSERT_OK(SendRequest(definition_request)); json response_b = json::parse(GetResponse()); @@ -690,11 +695,10 @@ TEST_F(VerilogLanguageServerSymbolTableTest, ASSERT_EQ(response_b["result"][0]["range"]["start"]["character"], 9); ASSERT_EQ(response_b["result"][0]["range"]["end"]["line"], 1); ASSERT_EQ(response_b["result"][0]["range"]["end"]["character"], 13); - ASSERT_EQ(response_b["result"][0]["uri"], "file://" + module_b.filename()); + ASSERT_EQ(response_b["result"][0]["uri"], module_b_uri); // find definition for "var1" variable in a.sv file - definition_request = - DefinitionRequest("file://" + module_a.filename(), 3, 2, 16); + definition_request = DefinitionRequest(module_a_uri, 3, 2, 16); ASSERT_OK(SendRequest(definition_request)); json response_a = json::parse(GetResponse()); @@ -705,7 +709,7 @@ TEST_F(VerilogLanguageServerSymbolTableTest, ASSERT_EQ(response_a["result"][0]["range"]["start"]["character"], 9); ASSERT_EQ(response_a["result"][0]["range"]["end"]["line"], 1); ASSERT_EQ(response_a["result"][0]["range"]["end"]["character"], 13); - ASSERT_EQ(response_a["result"][0]["uri"], "file://" + module_a.filename()); + ASSERT_EQ(response_a["result"][0]["uri"], module_a_uri); } // Check textDocument/definition request where we want definition of a symbol @@ -721,19 +725,20 @@ TEST_F(VerilogLanguageServerSymbolTableTest, const verible::file::testing::ScopedTestFile module_b(root_dir, kSampleModuleB, "b.sv"); + const std::string module_a_uri = PathToLSPUri(module_a.filename()); + const std::string module_b_uri = PathToLSPUri(module_b.filename()); const std::string module_a_open_request = - DidOpenRequest("file://" + module_a.filename(), kSampleModuleA); + DidOpenRequest(module_a_uri, kSampleModuleA); ASSERT_OK(SendRequest(module_a_open_request)); const std::string module_b_open_request = - DidOpenRequest("file://" + module_b.filename(), kSampleModuleB); + DidOpenRequest(module_b_uri, kSampleModuleB); ASSERT_OK(SendRequest(module_b_open_request)); // obtain diagnostics for both files GetResponse(); // find definition for "var1" variable in b.sv file - std::string definition_request = - DefinitionRequest("file://" + module_b.filename(), 2, 4, 14); + std::string definition_request = DefinitionRequest(module_b_uri, 2, 4, 14); ASSERT_OK(SendRequest(definition_request)); json response_b = json::parse(GetResponse()); @@ -744,7 +749,7 @@ TEST_F(VerilogLanguageServerSymbolTableTest, ASSERT_EQ(response_b["result"][0]["range"]["start"]["character"], 9); ASSERT_EQ(response_b["result"][0]["range"]["end"]["line"], 1); ASSERT_EQ(response_b["result"][0]["range"]["end"]["character"], 13); - ASSERT_EQ(response_b["result"][0]["uri"], "file://" + module_a.filename()); + ASSERT_EQ(response_b["result"][0]["uri"], module_a_uri); } // Check textDocument/definition request where we want definition of a symbol @@ -760,16 +765,17 @@ TEST_F(VerilogLanguageServerSymbolTableTest, const verible::file::testing::ScopedTestFile module_b(root_dir, kSampleModuleB, "b.sv"); + const std::string module_a_uri = PathToLSPUri(module_a.filename()); + const std::string module_b_uri = PathToLSPUri(module_b.filename()); const std::string module_b_open_request = - DidOpenRequest("file://" + module_b.filename(), kSampleModuleB); + DidOpenRequest(module_b_uri, kSampleModuleB); ASSERT_OK(SendRequest(module_b_open_request)); // obtain diagnostics for both files GetResponse(); // find definition for "var1" variable in b.sv file - std::string definition_request = - DefinitionRequest("file://" + module_b.filename(), 2, 4, 14); + std::string definition_request = DefinitionRequest(module_b_uri, 2, 4, 14); ASSERT_OK(SendRequest(definition_request)); json response_b = json::parse(GetResponse()); @@ -780,7 +786,7 @@ TEST_F(VerilogLanguageServerSymbolTableTest, ASSERT_EQ(response_b["result"][0]["range"]["start"]["character"], 9); ASSERT_EQ(response_b["result"][0]["range"]["end"]["line"], 1); ASSERT_EQ(response_b["result"][0]["range"]["end"]["character"], 13); - ASSERT_EQ(response_b["result"][0]["uri"], "file://" + module_a.filename()); + ASSERT_EQ(response_b["result"][0]["uri"], module_a_uri); } // Check textDocument/definition request where we want definition of a symbol @@ -796,11 +802,13 @@ TEST_F(VerilogLanguageServerSymbolTableTest, const verible::file::testing::ScopedTestFile module_b(root_dir, kSampleModuleB, "b.sv"); + const std::string module_a_uri = PathToLSPUri(module_a.filename()); + const std::string module_b_uri = PathToLSPUri(module_b.filename()); const std::string module_a_open_request = - DidOpenRequest("file://" + module_a.filename(), kSampleModuleA); + DidOpenRequest(module_a_uri, kSampleModuleA); ASSERT_OK(SendRequest(module_a_open_request)); const std::string module_b_open_request = - DidOpenRequest("file://" + module_b.filename(), kSampleModuleB); + DidOpenRequest(module_b_uri, kSampleModuleB); ASSERT_OK(SendRequest(module_b_open_request)); // Close a.sv from the Language Server perspective @@ -811,7 +819,7 @@ TEST_F(VerilogLanguageServerSymbolTableTest, {"params", {{"textDocument", { - {"uri", "file://" + module_a.filename()}, + {"uri", module_a_uri}, }}}}}.dump(); ASSERT_OK(SendRequest(closing_request)); @@ -819,8 +827,7 @@ TEST_F(VerilogLanguageServerSymbolTableTest, GetResponse(); // find definition for "var1" variable of a module in b.sv file - std::string definition_request = - DefinitionRequest("file://" + module_b.filename(), 2, 4, 14); + std::string definition_request = DefinitionRequest(module_b_uri, 2, 4, 14); ASSERT_OK(SendRequest(definition_request)); json response_b = json::parse(GetResponse()); @@ -831,7 +838,7 @@ TEST_F(VerilogLanguageServerSymbolTableTest, ASSERT_EQ(response_b["result"][0]["range"]["start"]["character"], 9); ASSERT_EQ(response_b["result"][0]["range"]["end"]["line"], 1); ASSERT_EQ(response_b["result"][0]["range"]["end"]["character"], 13); - ASSERT_EQ(response_b["result"][0]["uri"], "file://" + module_a.filename()); + ASSERT_EQ(response_b["result"][0]["uri"], module_a_uri); // perform double check ASSERT_OK(SendRequest(definition_request)); @@ -843,7 +850,7 @@ TEST_F(VerilogLanguageServerSymbolTableTest, ASSERT_EQ(response_b["result"][0]["range"]["start"]["character"], 9); ASSERT_EQ(response_b["result"][0]["range"]["end"]["line"], 1); ASSERT_EQ(response_b["result"][0]["range"]["end"]["character"], 13); - ASSERT_EQ(response_b["result"][0]["uri"], "file://" + module_a.filename()); + ASSERT_EQ(response_b["result"][0]["uri"], module_a_uri); } // Check textDocument/definition request where we want definition of a symbol @@ -869,16 +876,17 @@ endmodule const verible::file::testing::ScopedTestFile module_b( root_dir, sample_module_b_with_error, "b.sv"); + const std::string module_a_uri = PathToLSPUri(module_a.filename()); + const std::string module_b_uri = PathToLSPUri(module_b.filename()); const std::string module_a_open_request = - DidOpenRequest("file://" + module_a.filename(), kSampleModuleA); + DidOpenRequest(module_a_uri, kSampleModuleA); ASSERT_OK(SendRequest(module_a_open_request)); // obtain diagnostics for both files GetResponse(); // find definition for "var1" variable in a.sv file - std::string definition_request = - DefinitionRequest("file://" + module_a.filename(), 2, 2, 16); + std::string definition_request = DefinitionRequest(module_a_uri, 2, 2, 16); ASSERT_OK(SendRequest(definition_request)); json response = json::parse(GetResponse()); @@ -889,7 +897,7 @@ endmodule ASSERT_EQ(response["result"][0]["range"]["start"]["character"], 9); ASSERT_EQ(response["result"][0]["range"]["end"]["line"], 1); ASSERT_EQ(response["result"][0]["range"]["end"]["character"], 13); - ASSERT_EQ(response["result"][0]["uri"], "file://" + module_a.filename()); + ASSERT_EQ(response["result"][0]["uri"], module_a_uri); } // Check textDocument/definition request where we want definition of a symbol @@ -914,16 +922,17 @@ endmodule const verible::file::testing::ScopedTestFile module_b( root_dir, sample_module_b_with_error, "b.sv"); + const std::string module_a_uri = PathToLSPUri(module_a.filename()); + const std::string module_b_uri = PathToLSPUri(module_b.filename()); const std::string module_a_open_request = - DidOpenRequest("file://" + module_a.filename(), kSampleModuleA); + DidOpenRequest(module_a_uri, kSampleModuleA); ASSERT_OK(SendRequest(module_a_open_request)); // obtain diagnostics for both files GetResponse(); // find definition for "var1" variable of a module in b.sv file - std::string definition_request = - DefinitionRequest("file://" + module_b.filename(), 2, 4, 15); + std::string definition_request = DefinitionRequest(module_b_uri, 2, 4, 15); ASSERT_OK(SendRequest(definition_request)); json response_b = json::parse(GetResponse()); @@ -943,16 +952,17 @@ TEST_F(VerilogLanguageServerSymbolTableTest, DefinitionRequestUnsupportedURI) { const verible::file::testing::ScopedTestFile module_a(root_dir, kSampleModuleA, "a.sv"); + const std::string module_a_uri = PathToLSPUri(module_a.filename()); const std::string module_a_open_request = - DidOpenRequest("file://" + module_a.filename(), kSampleModuleA); + DidOpenRequest(module_a_uri, kSampleModuleA); ASSERT_OK(SendRequest(module_a_open_request)); // obtain diagnostics for both files GetResponse(); // find definition for "var1" variable in a.sv file - std::string definition_request = - DefinitionRequest("https://" + module_a.filename(), 2, 2, 16); + std::string definition_request = DefinitionRequest( + absl::StrReplaceAll(module_a_uri, {{"file://", "https://"}}), 2, 2, 16); ASSERT_OK(SendRequest(definition_request)); json response = json::parse(GetResponse()); @@ -971,16 +981,16 @@ TEST_F(VerilogLanguageServerSymbolTableTest, const verible::file::testing::ScopedTestFile module_a(root_dir, kSampleModuleA, "a.sv"); + const std::string module_a_uri = PathToLSPUri(module_a.filename()); const std::string module_a_open_request = - DidOpenRequest("file://" + module_a.filename(), kSampleModuleA); + DidOpenRequest(module_a_uri, kSampleModuleA); ASSERT_OK(SendRequest(module_a_open_request)); // obtain diagnostics for both files GetResponse(); // find definition for "var1" variable in a.sv file - std::string definition_request = - DefinitionRequest("file://" + module_a.filename(), 2, 1, 10); + std::string definition_request = DefinitionRequest(module_a_uri, 2, 1, 10); ASSERT_OK(SendRequest(definition_request)); json response = json::parse(GetResponse()); @@ -991,7 +1001,7 @@ TEST_F(VerilogLanguageServerSymbolTableTest, ASSERT_EQ(response["result"][0]["range"]["start"]["character"], 9); ASSERT_EQ(response["result"][0]["range"]["end"]["line"], 1); ASSERT_EQ(response["result"][0]["range"]["end"]["character"], 13); - ASSERT_EQ(response["result"][0]["uri"], "file://" + module_a.filename()); + ASSERT_EQ(response["result"][0]["uri"], module_a_uri); } // Check textDocument/definition when the cursor points at nothing @@ -1004,16 +1014,16 @@ TEST_F(VerilogLanguageServerSymbolTableTest, const verible::file::testing::ScopedTestFile module_a(root_dir, kSampleModuleA, "a.sv"); + const std::string module_a_uri = PathToLSPUri(module_a.filename()); const std::string module_a_open_request = - DidOpenRequest("file://" + module_a.filename(), kSampleModuleA); + DidOpenRequest(module_a_uri, kSampleModuleA); ASSERT_OK(SendRequest(module_a_open_request)); // obtain diagnostics for both files GetResponse(); // find definition for "var1" variable in a.sv file - std::string definition_request = - DefinitionRequest("file://" + module_a.filename(), 2, 1, 0); + std::string definition_request = DefinitionRequest(module_a_uri, 2, 1, 0); ASSERT_OK(SendRequest(definition_request)); json response = json::parse(GetResponse()); @@ -1032,16 +1042,16 @@ TEST_F(VerilogLanguageServerSymbolTableTest, const verible::file::testing::ScopedTestFile module_b(root_dir, kSampleModuleB, "b.sv"); + const std::string module_b_uri = PathToLSPUri(module_b.filename()); const std::string module_b_open_request = - DidOpenRequest("file://" + module_b.filename(), kSampleModuleB); + DidOpenRequest(module_b_uri, kSampleModuleB); ASSERT_OK(SendRequest(module_b_open_request)); // obtain diagnostics for both files GetResponse(); // find definition for "var1" variable in a.sv file - std::string definition_request = - DefinitionRequest("file://" + module_b.filename(), 2, 3, 2); + std::string definition_request = DefinitionRequest(module_b_uri, 2, 3, 2); ASSERT_OK(SendRequest(definition_request)); json response = json::parse(GetResponse()); @@ -1056,22 +1066,22 @@ TEST_F(VerilogLanguageServerSymbolTableTest, DefinitionRequestNoFileList) { const verible::file::testing::ScopedTestFile module_a(root_dir, kSampleModuleA, "a.sv"); + const std::string module_a_uri = PathToLSPUri(module_a.filename()); const std::string module_a_open_request = - DidOpenRequest("file://" + module_a.filename(), kSampleModuleA); + DidOpenRequest(module_a_uri, kSampleModuleA); ASSERT_OK(SendRequest(module_a_open_request)); // obtain diagnostics GetResponse(); // find definition for "var1" variable in a.sv file - std::string definition_request = - DefinitionRequest("file://" + module_a.filename(), 2, 2, 16); + std::string definition_request = DefinitionRequest(module_a_uri, 2, 2, 16); ASSERT_OK(SendRequest(definition_request)); json response = json::parse(GetResponse()); ASSERT_EQ(response["result"].size(), 1); - ASSERT_EQ(response["result"][0]["uri"], "file://" + module_a.filename()); + ASSERT_EQ(response["result"][0]["uri"], module_a_uri); } // Check textDocument/definition request where we want definition of a symbol @@ -1083,19 +1093,21 @@ TEST_F(VerilogLanguageServerSymbolTableTest, const verible::file::testing::ScopedTestFile module_b(root_dir, kSampleModuleB, "b.sv"); + const std::string module_a_uri = PathToLSPUri(module_a.filename()); + const std::string module_b_uri = PathToLSPUri(module_b.filename()); + const std::string module_a_open_request = - DidOpenRequest("file://" + module_a.filename(), kSampleModuleA); + DidOpenRequest(module_a_uri, kSampleModuleA); ASSERT_OK(SendRequest(module_a_open_request)); const std::string module_b_open_request = - DidOpenRequest("file://" + module_b.filename(), kSampleModuleB); + DidOpenRequest(module_b_uri, kSampleModuleB); ASSERT_OK(SendRequest(module_b_open_request)); // obtain diagnostics for both files GetResponse(); // find definition for "var1" variable in b.sv file - std::string definition_request = - DefinitionRequest("file://" + module_b.filename(), 2, 4, 14); + std::string definition_request = DefinitionRequest(module_b_uri, 2, 4, 14); ASSERT_OK(SendRequest(definition_request)); json response_b = json::parse(GetResponse()); @@ -1106,7 +1118,7 @@ TEST_F(VerilogLanguageServerSymbolTableTest, ASSERT_EQ(response_b["result"][0]["range"]["start"]["character"], 9); ASSERT_EQ(response_b["result"][0]["range"]["end"]["line"], 1); ASSERT_EQ(response_b["result"][0]["range"]["end"]["character"], 13); - ASSERT_EQ(response_b["result"][0]["uri"], "file://" + module_a.filename()); + ASSERT_EQ(response_b["result"][0]["uri"], module_a_uri); } TEST_F(VerilogLanguageServerSymbolTableTest, MultipleDefinitionsOfSameSymbol) { @@ -1139,15 +1151,16 @@ endmodule const verible::file::testing::ScopedTestFile module_foo(root_dir, foo, "foo.sv"); - const std::string foo_open_request = - DidOpenRequest("file://" + module_foo.filename(), foo); + const std::string module_foo_uri = PathToLSPUri(module_foo.filename()); + const std::string module_bar_1_uri = PathToLSPUri(module_bar_1.filename()); + + const std::string foo_open_request = DidOpenRequest(module_foo_uri, foo); ASSERT_OK(SendRequest(foo_open_request)); GetResponse(); // find definition for "bar" type - std::string definition_request = - DefinitionRequest("file://" + module_foo.filename(), 2, 1, 3); + std::string definition_request = DefinitionRequest(module_foo_uri, 2, 1, 3); ASSERT_OK(SendRequest(definition_request)); json response = json::parse(GetResponse()); @@ -1158,7 +1171,7 @@ endmodule ASSERT_EQ(response["result"][0]["range"]["start"]["character"], 7); ASSERT_EQ(response["result"][0]["range"]["end"]["line"], 0); ASSERT_EQ(response["result"][0]["range"]["end"]["character"], 10); - ASSERT_EQ(response["result"][0]["uri"], "file://" + module_bar_1.filename()); + ASSERT_EQ(response["result"][0]["uri"], module_bar_1_uri); } // Sample of badly styled modle @@ -1182,8 +1195,9 @@ bool CheckDiagnosticsContainLinterIssue(const json &diagnostics, TEST_F(VerilogLanguageServerSymbolTableTest, DefaultConfigurationTest) { const verible::file::testing::ScopedTestFile module_mod( root_dir, badly_styled_module, "my_mod.sv"); + const std::string mod_open_request = - DidOpenRequest("file://" + module_mod.filename(), badly_styled_module); + DidOpenRequest(PathToLSPUri(module_mod.filename()), badly_styled_module); ASSERT_OK(SendRequest(mod_open_request)); @@ -1211,7 +1225,7 @@ TEST_F(VerilogLanguageServerSymbolTableTest, ParsingLinterNoTabs) { const verible::file::testing::ScopedTestFile lint_file(root_dir, lint_config, ".rules.verible_lint"); const std::string mod_open_request = - DidOpenRequest("file://" + module_mod.filename(), badly_styled_module); + DidOpenRequest(PathToLSPUri(module_mod.filename()), badly_styled_module); ASSERT_OK(SendRequest(mod_open_request)); @@ -1241,7 +1255,7 @@ TEST_F(VerilogLanguageServerSymbolTableTest, const verible::file::testing::ScopedTestFile lint_file(root_dir, lint_config, ".rules.verible_lint"); const std::string mod_open_request = - DidOpenRequest("file://" + module_mod.filename(), badly_styled_module); + DidOpenRequest(PathToLSPUri(module_mod.filename()), badly_styled_module); ASSERT_OK(SendRequest(mod_open_request)); @@ -1293,50 +1307,50 @@ TEST_F(VerilogLanguageServerSymbolTableTest, const verible::file::testing::ScopedTestFile module_b(root_dir, kSampleModuleB, "b.sv"); + const std::string module_a_uri = PathToLSPUri(module_a.filename()); + const std::string module_b_uri = PathToLSPUri(module_b.filename()); + const std::string module_a_open_request = - DidOpenRequest("file://" + module_a.filename(), kSampleModuleA); + DidOpenRequest(module_a_uri, kSampleModuleA); ASSERT_OK(SendRequest(module_a_open_request)); const std::string module_b_open_request = - DidOpenRequest("file://" + module_b.filename(), kSampleModuleB); + DidOpenRequest(module_b_uri, kSampleModuleB); ASSERT_OK(SendRequest(module_b_open_request)); // obtain diagnostics for both files GetResponse(); // find references for "var1" variable in a.sv file - std::string references_request = - ReferencesRequest("file://" + module_a.filename(), 2, 1, 11); + std::string references_request = ReferencesRequest(module_a_uri, 2, 1, 11); ASSERT_OK(SendRequest(references_request)); json response_a = json::parse(GetResponse()); ASSERT_EQ(response_a["id"], 2); - json var1_a_refs = { - ReferenceEntry( - { - .line = 2, - .column = 16, - }, - {.line = 2, .column = 20}, "file://" + module_a.filename()), - ReferenceEntry( - { - .line = 1, - .column = 9, - }, - {.line = 1, .column = 13}, "file://" + module_a.filename()), - ReferenceEntry( - { - .line = 4, - .column = 14, - }, - {.line = 4, .column = 18}, "file://" + module_b.filename())}; + json var1_a_refs = {ReferenceEntry( + { + .line = 2, + .column = 16, + }, + {.line = 2, .column = 20}, module_a_uri), + ReferenceEntry( + { + .line = 1, + .column = 9, + }, + {.line = 1, .column = 13}, module_a_uri), + ReferenceEntry( + { + .line = 4, + .column = 14, + }, + {.line = 4, .column = 18}, module_b_uri)}; CheckReferenceResults(response_a["result"], var1_a_refs); // find references for "var1" variable in b.sv file - references_request = - ReferencesRequest("file://" + module_b.filename(), 3, 2, 18); + references_request = ReferencesRequest(module_b_uri, 3, 2, 18); ASSERT_OK(SendRequest(references_request)); json response_b = json::parse(GetResponse()); @@ -1349,13 +1363,13 @@ TEST_F(VerilogLanguageServerSymbolTableTest, .line = 1, .column = 9, }, - {.line = 1, .column = 13}, "file://" + module_b.filename()), + {.line = 1, .column = 13}, module_b_uri), ReferenceEntry( { .line = 2, .column = 16, }, - {.line = 2, .column = 20}, "file://" + module_b.filename()), + {.line = 2, .column = 20}, module_b_uri), }; CheckReferenceResults(response_b["result"], var1_b_refs); @@ -1370,16 +1384,17 @@ TEST_F(VerilogLanguageServerSymbolTableTest, CheckReferenceInvalidLocation) { const verible::file::testing::ScopedTestFile module_a(root_dir, kSampleModuleA, "a.sv"); + const std::string module_a_uri = PathToLSPUri(module_a.filename()); + const std::string module_a_open_request = - DidOpenRequest("file://" + module_a.filename(), kSampleModuleA); + DidOpenRequest(module_a_uri, kSampleModuleA); ASSERT_OK(SendRequest(module_a_open_request)); // obtain diagnostics for both files GetResponse(); // find references for "var1" variable in a.sv file - std::string references_request = - ReferencesRequest("file://" + module_a.filename(), 2, 1, 0); + std::string references_request = ReferencesRequest(module_a_uri, 2, 1, 0); ASSERT_OK(SendRequest(references_request)); json response_a = json::parse(GetResponse()); @@ -1397,16 +1412,17 @@ TEST_F(VerilogLanguageServerSymbolTableTest, CheckReferenceKeyword) { const verible::file::testing::ScopedTestFile module_a(root_dir, kSampleModuleA, "a.sv"); + const std::string module_a_uri = PathToLSPUri(module_a.filename()); + const std::string module_a_open_request = - DidOpenRequest("file://" + module_a.filename(), kSampleModuleA); + DidOpenRequest(module_a_uri, kSampleModuleA); ASSERT_OK(SendRequest(module_a_open_request)); // obtain diagnostics for both files GetResponse(); // find references for "var1" variable in a.sv file - std::string references_request = - ReferencesRequest("file://" + module_a.filename(), 2, 1, 5); + std::string references_request = ReferencesRequest(module_a_uri, 2, 1, 5); ASSERT_OK(SendRequest(references_request)); json response_a = json::parse(GetResponse()); @@ -1424,16 +1440,17 @@ TEST_F(VerilogLanguageServerSymbolTableTest, CheckReferenceUnknownSymbol) { const verible::file::testing::ScopedTestFile module_b(root_dir, kSampleModuleB, "b.sv"); + const std::string module_b_uri = PathToLSPUri(module_b.filename()); + const std::string module_b_open_request = - DidOpenRequest("file://" + module_b.filename(), kSampleModuleB); + DidOpenRequest(module_b_uri, kSampleModuleB); ASSERT_OK(SendRequest(module_b_open_request)); // obtain diagnostics for both files GetResponse(); // find references for "var1" variable in a.sv file - std::string references_request = - ReferencesRequest("file://" + module_b.filename(), 2, 4, 16); + std::string references_request = ReferencesRequest(module_b_uri, 2, 4, 16); ASSERT_OK(SendRequest(references_request)); json response_b = json::parse(GetResponse());