Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/CLI/src/VfsNavigator.cpp
2725 views
1
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
2
#include "Luau/VfsNavigator.h"
3
4
#include "Luau/Common.h"
5
#include "Luau/Config.h"
6
#include "Luau/FileUtils.h"
7
#include "Luau/LuauConfig.h"
8
9
#include <array>
10
#include <string>
11
#include <string_view>
12
13
const std::array<std::string_view, 2> kSuffixes = {".luau", ".lua"};
14
const std::array<std::string_view, 2> kInitSuffixes = {"/init.luau", "/init.lua"};
15
16
struct ResolvedRealPath
17
{
18
NavigationStatus status;
19
std::string realPath;
20
};
21
22
static ResolvedRealPath getRealPath(std::string modulePath)
23
{
24
bool found = false;
25
std::string suffix;
26
27
size_t lastSlash = modulePath.find_last_of('/');
28
LUAU_ASSERT(lastSlash != std::string::npos);
29
std::string lastComponent = modulePath.substr(lastSlash + 1);
30
31
if (lastComponent != "init")
32
{
33
for (std::string_view potentialSuffix : kSuffixes)
34
{
35
if (isFile(modulePath + std::string(potentialSuffix)))
36
{
37
if (found)
38
return {NavigationStatus::Ambiguous};
39
40
suffix = potentialSuffix;
41
found = true;
42
}
43
}
44
}
45
if (isDirectory(modulePath))
46
{
47
if (found)
48
return {NavigationStatus::Ambiguous};
49
50
for (std::string_view potentialSuffix : kInitSuffixes)
51
{
52
if (isFile(modulePath + std::string(potentialSuffix)))
53
{
54
if (found)
55
return {NavigationStatus::Ambiguous};
56
57
suffix = potentialSuffix;
58
found = true;
59
}
60
}
61
62
found = true;
63
}
64
65
if (!found)
66
return {NavigationStatus::NotFound};
67
68
return {NavigationStatus::Success, modulePath + suffix};
69
}
70
71
static bool hasSuffix(std::string_view str, std::string_view suffix)
72
{
73
return str.size() >= suffix.size() && str.substr(str.size() - suffix.size()) == suffix;
74
}
75
76
static std::string getModulePath(std::string filePath)
77
{
78
for (char& c : filePath)
79
{
80
if (c == '\\')
81
c = '/';
82
}
83
84
std::string_view pathView = filePath;
85
86
if (isAbsolutePath(pathView))
87
{
88
size_t firstSlash = pathView.find_first_of('/');
89
LUAU_ASSERT(firstSlash != std::string::npos);
90
pathView.remove_prefix(firstSlash);
91
}
92
93
for (std::string_view suffix : kInitSuffixes)
94
{
95
if (hasSuffix(pathView, suffix))
96
{
97
pathView.remove_suffix(suffix.size());
98
return std::string(pathView);
99
}
100
}
101
for (std::string_view suffix : kSuffixes)
102
{
103
if (hasSuffix(pathView, suffix))
104
{
105
pathView.remove_suffix(suffix.size());
106
return std::string(pathView);
107
}
108
}
109
110
return std::string(pathView);
111
}
112
113
NavigationStatus VfsNavigator::updateRealPaths()
114
{
115
ResolvedRealPath result = getRealPath(modulePath);
116
ResolvedRealPath absoluteResult = getRealPath(absoluteModulePath);
117
if (result.status != NavigationStatus::Success || absoluteResult.status != NavigationStatus::Success)
118
return result.status;
119
120
realPath = isAbsolutePath(result.realPath) ? absolutePathPrefix + result.realPath : result.realPath;
121
absoluteRealPath = absolutePathPrefix + absoluteResult.realPath;
122
return NavigationStatus::Success;
123
}
124
125
NavigationStatus VfsNavigator::resetToStdIn()
126
{
127
std::optional<std::string> cwd = getCurrentWorkingDirectory();
128
if (!cwd)
129
return NavigationStatus::NotFound;
130
131
realPath = "./stdin";
132
absoluteRealPath = normalizePath(*cwd + "/stdin");
133
modulePath = "./stdin";
134
absoluteModulePath = getModulePath(absoluteRealPath);
135
136
size_t firstSlash = absoluteRealPath.find_first_of('/');
137
LUAU_ASSERT(firstSlash != std::string::npos);
138
absolutePathPrefix = absoluteRealPath.substr(0, firstSlash);
139
140
return NavigationStatus::Success;
141
}
142
143
NavigationStatus VfsNavigator::resetToPath(const std::string& path)
144
{
145
std::string normalizedPath = normalizePath(path);
146
147
if (isAbsolutePath(normalizedPath))
148
{
149
modulePath = getModulePath(normalizedPath);
150
absoluteModulePath = modulePath;
151
152
size_t firstSlash = normalizedPath.find_first_of('/');
153
LUAU_ASSERT(firstSlash != std::string::npos);
154
absolutePathPrefix = normalizedPath.substr(0, firstSlash);
155
}
156
else
157
{
158
std::optional<std::string> cwd = getCurrentWorkingDirectory();
159
if (!cwd)
160
return NavigationStatus::NotFound;
161
162
modulePath = getModulePath(normalizedPath);
163
std::string joinedPath = normalizePath(*cwd + "/" + normalizedPath);
164
absoluteModulePath = getModulePath(joinedPath);
165
166
size_t firstSlash = joinedPath.find_first_of('/');
167
LUAU_ASSERT(firstSlash != std::string::npos);
168
absolutePathPrefix = joinedPath.substr(0, firstSlash);
169
}
170
171
return updateRealPaths();
172
}
173
174
NavigationStatus VfsNavigator::toParent()
175
{
176
if (absoluteModulePath == "/")
177
return NavigationStatus::NotFound;
178
179
size_t numSlashes = 0;
180
for (char c : absoluteModulePath)
181
{
182
if (c == '/')
183
numSlashes++;
184
}
185
LUAU_ASSERT(numSlashes > 0);
186
187
if (numSlashes == 1)
188
return NavigationStatus::NotFound;
189
190
modulePath = normalizePath(modulePath + "/..");
191
absoluteModulePath = normalizePath(absoluteModulePath + "/..");
192
193
// There is no ambiguity when navigating up in a tree.
194
NavigationStatus status = updateRealPaths();
195
return status == NavigationStatus::Ambiguous ? NavigationStatus::Success : status;
196
}
197
198
NavigationStatus VfsNavigator::toChild(const std::string& name)
199
{
200
if (name == ".config")
201
return NavigationStatus::NotFound;
202
203
modulePath = normalizePath(modulePath + "/" + name);
204
absoluteModulePath = normalizePath(absoluteModulePath + "/" + name);
205
206
return updateRealPaths();
207
}
208
209
std::string VfsNavigator::getFilePath() const
210
{
211
return realPath;
212
}
213
214
std::string VfsNavigator::getAbsoluteFilePath() const
215
{
216
return absoluteRealPath;
217
}
218
219
std::string VfsNavigator::getConfigPath(const std::string& filename) const
220
{
221
std::string_view directory = realPath;
222
223
for (std::string_view suffix : kInitSuffixes)
224
{
225
if (hasSuffix(directory, suffix))
226
{
227
directory.remove_suffix(suffix.size());
228
return std::string(directory) + '/' + filename;
229
}
230
}
231
for (std::string_view suffix : kSuffixes)
232
{
233
if (hasSuffix(directory, suffix))
234
{
235
directory.remove_suffix(suffix.size());
236
return std::string(directory) + '/' + filename;
237
}
238
}
239
240
return std::string(directory) + '/' + filename;
241
}
242
243
VfsNavigator::ConfigStatus VfsNavigator::getConfigStatus() const
244
{
245
bool luaurcExists = isFile(getConfigPath(Luau::kConfigName));
246
bool luauConfigExists = isFile(getConfigPath(Luau::kLuauConfigName));
247
248
if (luaurcExists && luauConfigExists)
249
return ConfigStatus::Ambiguous;
250
else if (luauConfigExists)
251
return ConfigStatus::PresentLuau;
252
else if (luaurcExists)
253
return ConfigStatus::PresentJson;
254
else
255
return ConfigStatus::Absent;
256
}
257
258
std::optional<std::string> VfsNavigator::getConfig() const
259
{
260
ConfigStatus status = getConfigStatus();
261
LUAU_ASSERT(status == ConfigStatus::PresentJson || status == ConfigStatus::PresentLuau);
262
263
if (status == ConfigStatus::PresentJson)
264
return readFile(getConfigPath(Luau::kConfigName));
265
else if (status == ConfigStatus::PresentLuau)
266
return readFile(getConfigPath(Luau::kLuauConfigName));
267
268
LUAU_UNREACHABLE();
269
}
270
271