22
22
#include < libsolutil/StringUtils.h>
23
23
#include < libsolutil/CommonIO.h>
24
24
25
+ #include < range/v3/algorithm/none_of.hpp>
25
26
#include < range/v3/range/conversion.hpp>
26
27
#include < range/v3/view/transform.hpp>
27
28
28
29
#include < regex>
29
30
31
+ #include < boost/algorithm/string/predicate.hpp>
32
+
30
33
using namespace std ;
31
34
using namespace solidity ;
32
35
using namespace solidity ::lsp;
33
36
using namespace solidity ::frontend;
34
37
35
38
using solidity::util::readFileAsString;
36
39
using solidity::util::joinHumanReadable;
40
+ using solidity::util::Result;
37
41
38
- FileRepository::FileRepository (boost::filesystem::path _basePath): m_basePath(std::move(_basePath))
42
+ FileRepository::FileRepository (boost::filesystem::path _basePath, std::vector<boost::filesystem::path> _includePaths):
43
+ m_basePath(std::move(_basePath)),
44
+ m_includePaths(std::move(_includePaths))
39
45
{
40
46
}
41
47
48
+ void FileRepository::setIncludePaths (std::vector<boost::filesystem::path> _paths)
49
+ {
50
+ m_includePaths = std::move (_paths);
51
+ }
52
+
42
53
string FileRepository::sourceUnitNameToUri (string const & _sourceUnitName) const
43
54
{
44
55
regex const windowsDriveLetterPath (" ^[a-zA-Z]:/" );
45
56
57
+ auto const ensurePathIsUnixLike = [&](string inputPath) -> string {
58
+ if (!regex_search (inputPath, windowsDriveLetterPath))
59
+ return inputPath;
60
+ else
61
+ return " /" + move (inputPath);
62
+ };
63
+
46
64
if (m_sourceUnitNamesToUri.count (_sourceUnitName))
65
+ {
66
+ solAssert (boost::starts_with (m_sourceUnitNamesToUri.at (_sourceUnitName), " file://" ), " " );
47
67
return m_sourceUnitNamesToUri.at (_sourceUnitName);
68
+ }
48
69
else if (_sourceUnitName.find (" file://" ) == 0 )
49
70
return _sourceUnitName;
50
71
else if (regex_search (_sourceUnitName, windowsDriveLetterPath))
51
72
return " file:///" + _sourceUnitName;
52
- else if (_sourceUnitName.find (" /" ) == 0 )
53
- return " file://" + _sourceUnitName;
54
- else
73
+ else if (
74
+ auto const resolvedPath = tryResolvePath (_sourceUnitName);
75
+ resolvedPath.message ().empty ()
76
+ )
77
+ return " file://" + ensurePathIsUnixLike (resolvedPath.get ().generic_string ());
78
+ else if (m_basePath.generic_string () != " /" )
55
79
return " file://" + m_basePath.generic_string () + " /" + _sourceUnitName;
80
+ else
81
+ // Avoid double-/ in case base-path itself is simply a UNIX root filesystem root.
82
+ return " file:///" + _sourceUnitName;
56
83
}
57
84
58
85
string FileRepository::uriToSourceUnitName (string const & _path) const
@@ -69,6 +96,52 @@ void FileRepository::setSourceByUri(string const& _uri, string _source)
69
96
m_sourceCodes[sourceUnitName] = std::move (_source);
70
97
}
71
98
99
+ Result<boost::filesystem::path> FileRepository::tryResolvePath (std::string const & _strippedSourceUnitName) const
100
+ {
101
+ if (
102
+ boost::filesystem::path (_strippedSourceUnitName).has_root_path () &&
103
+ boost::filesystem::exists (_strippedSourceUnitName)
104
+ )
105
+ return boost::filesystem::path (_strippedSourceUnitName);
106
+
107
+ vector<boost::filesystem::path> candidates;
108
+ vector<reference_wrapper<boost::filesystem::path const >> prefixes = {m_basePath};
109
+ prefixes += (m_includePaths | ranges::to<vector<reference_wrapper<boost::filesystem::path const >>>);
110
+ auto const defaultInclude = m_basePath / " node_modules" ;
111
+ if (m_includePaths.empty ())
112
+ prefixes.emplace_back (defaultInclude);
113
+
114
+ auto const pathToQuotedString = [](boost::filesystem::path const & _path) { return " \" " + _path.string () + " \" " ; };
115
+
116
+ for (auto const & prefix: prefixes)
117
+ {
118
+ boost::filesystem::path canonicalPath = boost::filesystem::path (prefix) / boost::filesystem::path (_strippedSourceUnitName);
119
+
120
+ if (boost::filesystem::exists (canonicalPath))
121
+ candidates.push_back (move (canonicalPath));
122
+ }
123
+
124
+ if (candidates.empty ())
125
+ return Result<boost::filesystem::path>::err (
126
+ " File not found. Searched the following locations: " +
127
+ joinHumanReadable (prefixes | ranges::views::transform (pathToQuotedString), " , " ) +
128
+ " ."
129
+ );
130
+
131
+ if (candidates.size () >= 2 )
132
+ return Result<boost::filesystem::path>::err (
133
+ " Ambiguous import. "
134
+ " Multiple matching files found inside base path and/or include paths: " +
135
+ joinHumanReadable (candidates | ranges::views::transform (pathToQuotedString), " , " ) +
136
+ " ."
137
+ );
138
+
139
+ if (!boost::filesystem::is_regular_file (candidates[0 ]))
140
+ return Result<boost::filesystem::path>::err (" Not a valid file." );
141
+
142
+ return candidates[0 ];
143
+ }
144
+
72
145
frontend::ReadCallback::Result FileRepository::readFile (string const & _kind, string const & _sourceUnitName)
73
146
{
74
147
solAssert (
@@ -83,53 +156,11 @@ frontend::ReadCallback::Result FileRepository::readFile(string const& _kind, str
83
156
return ReadCallback::Result{true , m_sourceCodes.at (_sourceUnitName)};
84
157
85
158
string const strippedSourceUnitName = stripFileUriSchemePrefix (_sourceUnitName);
159
+ Result<boost::filesystem::path> const resolvedPath = tryResolvePath (strippedSourceUnitName);
160
+ if (!resolvedPath.message ().empty ())
161
+ return ReadCallback::Result{false , resolvedPath.message ()};
86
162
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 ]);
163
+ auto contents = readFileAsString (resolvedPath.get ());
133
164
solAssert (m_sourceCodes.count (_sourceUnitName) == 0 , " " );
134
165
m_sourceCodes[_sourceUnitName] = contents;
135
166
return ReadCallback::Result{true , move (contents)};
0 commit comments