|
17 | 17 | // SPDX-License-Identifier: GPL-3.0
|
18 | 18 |
|
19 | 19 | #include <libsolidity/lsp/FileRepository.h>
|
| 20 | +#include <libsolidity/lsp/Utils.h> |
| 21 | + |
| 22 | +#include <libsolutil/StringUtils.h> |
| 23 | +#include <libsolutil/CommonIO.h> |
| 24 | + |
| 25 | +#include <range/v3/range/conversion.hpp> |
| 26 | +#include <range/v3/view/transform.hpp> |
| 27 | + |
| 28 | +#include <regex> |
20 | 29 |
|
21 | 30 | using namespace std;
|
22 | 31 | using namespace solidity;
|
23 | 32 | using namespace solidity::lsp;
|
| 33 | +using namespace solidity::frontend; |
24 | 34 |
|
25 |
| -namespace |
26 |
| -{ |
| 35 | +using solidity::util::readFileAsString; |
| 36 | +using solidity::util::joinHumanReadable; |
27 | 37 |
|
28 |
| -string stripFilePrefix(string const& _path) |
| 38 | +FileRepository::FileRepository(boost::filesystem::path _basePath): m_basePath(std::move(_basePath)) |
29 | 39 | {
|
30 |
| - if (_path.find("file://") == 0) |
31 |
| - return _path.substr(7); |
32 |
| - else |
33 |
| - return _path; |
34 |
| -} |
35 |
| - |
36 | 40 | }
|
37 | 41 |
|
38 |
| -string FileRepository::sourceUnitNameToClientPath(string const& _sourceUnitName) const |
| 42 | +string FileRepository::sourceUnitNameToUri(string const& _sourceUnitName) const |
39 | 43 | {
|
40 |
| - if (m_sourceUnitNamesToClientPaths.count(_sourceUnitName)) |
41 |
| - return m_sourceUnitNamesToClientPaths.at(_sourceUnitName); |
| 44 | + regex const windowsDriveLetterPath("^[a-zA-Z]:/"); |
| 45 | + |
| 46 | + if (m_sourceUnitNamesToUri.count(_sourceUnitName)) |
| 47 | + return m_sourceUnitNamesToUri.at(_sourceUnitName); |
42 | 48 | else if (_sourceUnitName.find("file://") == 0)
|
43 | 49 | return _sourceUnitName;
|
| 50 | + else if (regex_search(_sourceUnitName, windowsDriveLetterPath)) |
| 51 | + return "file:///" + _sourceUnitName; |
| 52 | + else if (_sourceUnitName.find("/") == 0) |
| 53 | + return "file://" + _sourceUnitName; |
44 | 54 | else
|
45 |
| - return "file://" + (m_fileReader.basePath() / _sourceUnitName).generic_string(); |
| 55 | + return "file://" + m_basePath.generic_string() + "/" + _sourceUnitName; |
46 | 56 | }
|
47 | 57 |
|
48 |
| -string FileRepository::clientPathToSourceUnitName(string const& _path) const |
| 58 | +string FileRepository::uriToSourceUnitName(string const& _path) const |
49 | 59 | {
|
50 |
| - return m_fileReader.cliPathToSourceUnitName(stripFilePrefix(_path)); |
| 60 | + return stripFileUriSchemePrefix(_path); |
51 | 61 | }
|
52 | 62 |
|
53 |
| -map<string, string> const& FileRepository::sourceUnits() const |
| 63 | +void FileRepository::setSourceByUri(string const& _uri, string _source) |
54 | 64 | {
|
55 |
| - return m_fileReader.sourceUnits(); |
| 65 | + // This is needed for uris outside the base path. It can lead to collisions, |
| 66 | + // but we need to mostly rewrite this in a future version anyway. |
| 67 | + auto sourceUnitName = uriToSourceUnitName(_uri); |
| 68 | + m_sourceUnitNamesToUri.emplace(sourceUnitName, _uri); |
| 69 | + m_sourceCodes[sourceUnitName] = std::move(_source); |
56 | 70 | }
|
57 | 71 |
|
58 |
| -void FileRepository::setSourceByClientPath(string const& _uri, string _text) |
| 72 | +frontend::ReadCallback::Result FileRepository::readFile(string const& _kind, string const& _sourceUnitName) |
59 | 73 | {
|
60 |
| - // This is needed for uris outside the base path. It can lead to collisions, |
61 |
| - // but we need to mostly rewrite this in a future version anyway. |
62 |
| - m_sourceUnitNamesToClientPaths.emplace(clientPathToSourceUnitName(_uri), _uri); |
63 |
| - m_fileReader.addOrUpdateFile(stripFilePrefix(_uri), move(_text)); |
| 74 | + solAssert( |
| 75 | + _kind == ReadCallback::kindString(ReadCallback::Kind::ReadFile), |
| 76 | + "ReadFile callback used as callback kind " + _kind |
| 77 | + ); |
| 78 | + |
| 79 | + try |
| 80 | + { |
| 81 | + // File was read already. Use local store. |
| 82 | + if (m_sourceCodes.count(_sourceUnitName)) |
| 83 | + return ReadCallback::Result{true, m_sourceCodes.at(_sourceUnitName)}; |
| 84 | + |
| 85 | + string const strippedSourceUnitName = stripFileUriSchemePrefix(_sourceUnitName); |
| 86 | + |
| 87 | + if ( |
| 88 | + boost::filesystem::path(strippedSourceUnitName).has_root_path() && |
| 89 | + boost::filesystem::exists(strippedSourceUnitName) |
| 90 | + ) |
| 91 | + { |
| 92 | + auto contents = readFileAsString(strippedSourceUnitName); |
| 93 | + solAssert(m_sourceCodes.count(_sourceUnitName) == 0, ""); |
| 94 | + m_sourceCodes[_sourceUnitName] = contents; |
| 95 | + return ReadCallback::Result{true, move(contents)}; |
| 96 | + } |
| 97 | + |
| 98 | + vector<boost::filesystem::path> candidates; |
| 99 | + vector<reference_wrapper<boost::filesystem::path>> prefixes = {m_basePath}; |
| 100 | + prefixes += (m_includePaths | ranges::to<vector<reference_wrapper<boost::filesystem::path>>>); |
| 101 | + |
| 102 | + auto const pathToQuotedString = [](boost::filesystem::path const& _path) { return "\"" + _path.string() + "\""; }; |
| 103 | + |
| 104 | + for (auto const& prefix: prefixes) |
| 105 | + { |
| 106 | + boost::filesystem::path canonicalPath = boost::filesystem::path(prefix) / boost::filesystem::path(strippedSourceUnitName); |
| 107 | + |
| 108 | + if (boost::filesystem::exists(canonicalPath)) |
| 109 | + candidates.push_back(move(canonicalPath)); |
| 110 | + } |
| 111 | + |
| 112 | + if (candidates.empty()) |
| 113 | + return ReadCallback::Result{ |
| 114 | + false, |
| 115 | + "File not found. Searched the following locations: " + |
| 116 | + joinHumanReadable(prefixes | ranges::views::transform(pathToQuotedString), ", ") + |
| 117 | + "." |
| 118 | + }; |
| 119 | + |
| 120 | + if (candidates.size() >= 2) |
| 121 | + return ReadCallback::Result{ |
| 122 | + false, |
| 123 | + "Ambiguous import. " |
| 124 | + "Multiple matching files found inside base path and/or include paths: " + |
| 125 | + joinHumanReadable(candidates | ranges::views::transform(pathToQuotedString), ", ") + |
| 126 | + "." |
| 127 | + }; |
| 128 | + |
| 129 | + if (!boost::filesystem::is_regular_file(candidates[0])) |
| 130 | + return ReadCallback::Result{false, "Not a valid file."}; |
| 131 | + |
| 132 | + auto contents = readFileAsString(candidates[0]); |
| 133 | + solAssert(m_sourceCodes.count(_sourceUnitName) == 0, ""); |
| 134 | + m_sourceCodes[_sourceUnitName] = contents; |
| 135 | + return ReadCallback::Result{true, move(contents)}; |
| 136 | + } |
| 137 | + catch (std::exception const& _exception) |
| 138 | + { |
| 139 | + return ReadCallback::Result{false, "Exception in read callback: " + boost::diagnostic_information(_exception)}; |
| 140 | + } |
| 141 | + catch (...) |
| 142 | + { |
| 143 | + return ReadCallback::Result{false, "Unknown exception in read callback: " + boost::current_exception_diagnostic_information()}; |
| 144 | + } |
64 | 145 | }
|
| 146 | + |
0 commit comments