Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Analysis/src/FileResolver.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/FileResolver.h"
3
4
#include "Luau/Common.h"
5
#include "Luau/StringUtils.h"
6
7
#include <memory>
8
#include <optional>
9
#include <string_view>
10
#include <utility>
11
12
namespace Luau
13
{
14
15
static std::optional<RequireSuggestions> processRequireSuggestions(std::optional<RequireSuggestions> suggestions)
16
{
17
if (!suggestions)
18
return suggestions;
19
20
for (RequireSuggestion& suggestion : *suggestions)
21
{
22
suggestion.fullPath = escape(suggestion.fullPath);
23
}
24
25
return suggestions;
26
}
27
28
static RequireSuggestions makeSuggestionsFromAliases(std::vector<RequireAlias> aliases)
29
{
30
RequireSuggestions result;
31
for (RequireAlias& alias : aliases)
32
{
33
RequireSuggestion suggestion;
34
suggestion.label = "@" + std::move(alias.alias);
35
suggestion.fullPath = suggestion.label;
36
suggestion.tags = std::move(alias.tags);
37
result.push_back(std::move(suggestion));
38
}
39
return result;
40
}
41
42
static RequireSuggestions makeSuggestionsForFirstComponent(std::unique_ptr<RequireNode> node)
43
{
44
RequireSuggestions result = makeSuggestionsFromAliases(node->getAvailableAliases());
45
result.push_back(RequireSuggestion{"./", "./", {}});
46
result.push_back(RequireSuggestion{"../", "../", {}});
47
return result;
48
}
49
50
static RequireSuggestions makeSuggestionsFromNode(std::unique_ptr<RequireNode> node, const std::string_view path, bool isPartialPath)
51
{
52
LUAU_ASSERT(!path.empty());
53
54
RequireSuggestions result;
55
56
const size_t lastSlashInPath = path.find_last_of('/');
57
58
if (lastSlashInPath != std::string_view::npos)
59
{
60
// Add a suggestion for the parent directory
61
RequireSuggestion parentSuggestion;
62
parentSuggestion.label = "..";
63
64
if (lastSlashInPath >= 2 && path.substr(lastSlashInPath - 2, 3) == "../")
65
{
66
parentSuggestion.fullPath = path.substr(0, lastSlashInPath + 1);
67
parentSuggestion.fullPath += "..";
68
}
69
else
70
{
71
parentSuggestion.fullPath = path.substr(0, lastSlashInPath);
72
}
73
74
result.push_back(std::move(parentSuggestion));
75
}
76
77
std::string fullPathPrefix;
78
if (isPartialPath)
79
{
80
// ./path/to/chi -> ./path/to/
81
fullPathPrefix += path.substr(0, lastSlashInPath + 1);
82
}
83
else
84
{
85
if (path.back() == '/')
86
{
87
// ./path/to/ -> ./path/to/
88
fullPathPrefix += path;
89
}
90
else
91
{
92
// ./path/to -> ./path/to/
93
fullPathPrefix += path;
94
fullPathPrefix += "/";
95
}
96
}
97
98
for (const std::unique_ptr<RequireNode>& child : node->getChildren())
99
{
100
if (!child)
101
continue;
102
103
std::string pathComponent = child->getPathComponent();
104
105
// If path component contains a slash, it cannot be required by string.
106
// There's no point suggesting it.
107
if (pathComponent.find('/') != std::string::npos)
108
continue;
109
110
RequireSuggestion suggestion;
111
suggestion.label = isPartialPath || path.back() == '/' ? child->getLabel() : "/" + child->getLabel();
112
suggestion.fullPath = fullPathPrefix + std::move(pathComponent);
113
suggestion.tags = child->getTags();
114
result.push_back(std::move(suggestion));
115
}
116
117
return result;
118
}
119
120
std::optional<RequireSuggestions> RequireSuggester::getRequireSuggestionsImpl(
121
const ModuleName& requirer,
122
const std::optional<std::string>& path
123
) const
124
{
125
if (!path)
126
return std::nullopt;
127
128
std::unique_ptr<RequireNode> requirerNode = getNode(requirer);
129
if (!requirerNode)
130
return std::nullopt;
131
132
const size_t slashPos = path->find_last_of('/');
133
134
if (slashPos == std::string::npos)
135
return makeSuggestionsForFirstComponent(std::move(requirerNode));
136
137
// If path already points at a Node, return the Node's children as paths.
138
if (std::unique_ptr<RequireNode> node = requirerNode->resolvePathToNode(*path))
139
return makeSuggestionsFromNode(std::move(node), *path, /* isPartialPath = */ false);
140
141
// Otherwise, recover a partial path and use this to generate suggestions.
142
if (std::unique_ptr<RequireNode> partialNode = requirerNode->resolvePathToNode(path->substr(0, slashPos)))
143
return makeSuggestionsFromNode(std::move(partialNode), *path, /* isPartialPath = */ true);
144
145
return std::nullopt;
146
}
147
148
std::optional<RequireSuggestions> RequireSuggester::getRequireSuggestions(const ModuleName& requirer, const std::optional<std::string>& path) const
149
{
150
return processRequireSuggestions(getRequireSuggestionsImpl(requirer, path));
151
}
152
153
std::optional<RequireSuggestions> FileResolver::getRequireSuggestions(const ModuleName& requirer, const std::optional<std::string>& path) const
154
{
155
return requireSuggester ? requireSuggester->getRequireSuggestions(requirer, path) : std::nullopt;
156
}
157
158
} // namespace Luau
159
160