Skip to content

Commit 6af0cc3

Browse files
authored
Feature: Support DM info from companion plugin even if no sourcemap.json exists (#1216)
* Populate a default sourcemap if plugin provides DM info with no sourcemap.json present * Add info popup when using only plugin info * Add unit test
1 parent 37c6892 commit 6af0cc3

File tree

4 files changed

+74
-12
lines changed

4 files changed

+74
-12
lines changed

src/include/Platform/RobloxPlatform.hpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,6 @@ class RobloxPlatform : public LSPPlatform
106106
private:
107107
// Plugin-provided DataModel information
108108
PluginNode* pluginInfo = nullptr;
109-
Luau::TypedAllocator<PluginNode> pluginNodeAllocator;
110109

111110
mutable std::unordered_map<Uri, const SourceNode*, UriHash> realPathsToSourceNodes{};
112111
mutable std::unordered_map<Luau::ModuleName, const SourceNode*> virtualPathsToSourceNodes{};
@@ -117,19 +116,24 @@ class RobloxPlatform : public LSPPlatform
117116
static Luau::ModuleName getVirtualPathFromSourceNode(const SourceNode* sourceNode);
118117

119118
void clearSourcemapTypes();
120-
bool updateSourceMap();
121119

122120
public:
123121
// The root source node from a parsed Rojo source map
124122
SourceNode* rootSourceNode = nullptr;
125123
Luau::TypedAllocator<SourceNode> sourceNodeAllocator;
124+
Luau::TypedAllocator<PluginNode> pluginNodeAllocator;
126125

127126
Luau::TypeArena instanceTypes;
128127

129-
/// For testing only
128+
/// These are "private" but exposed for unit testing only
129+
void setPluginInfo(PluginNode* info)
130+
{
131+
pluginInfo = info;
132+
}
133+
bool updateSourceMap();
130134
bool updateSourceMapFromContents(const std::string& sourceMapContents);
131-
/// For testing only
132135
void writePathsToMap(SourceNode* node, const std::string& base);
136+
133137
std::optional<Uri> getRealPathFromSourceNode(const SourceNode* sourceNode) const;
134138

135139
void mutateRegisteredDefinitions(Luau::GlobalTypes& globals, std::optional<nlohmann::json> metadata) override;

src/platform/roblox/RobloxSourcemap.cpp

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -445,18 +445,33 @@ bool RobloxPlatform::updateSourceMap()
445445
auto config = workspaceFolder->client->getConfiguration(workspaceFolder->rootUri);
446446
std::string sourcemapFileName = config.sourcemap.sourcemapFile;
447447

448+
// TODO: we assume the sourcemap file is in the workspace root
448449
auto sourcemapPath = workspaceFolder->rootUri.resolvePath(sourcemapFileName);
449-
workspaceFolder->client->sendTrace("Updating sourcemap contents from " + sourcemapPath.toString());
450450

451-
// Read in the sourcemap
452-
// TODO: we assume a sourcemap file in the workspace root
453-
if (auto sourceMapContents = Luau::FileUtils::readFile(sourcemapPath.fsPath()))
451+
if (Luau::FileUtils::exists(sourcemapPath.fsPath()))
454452
{
455-
return updateSourceMapFromContents(sourceMapContents.value());
453+
if (auto sourceMapContents = Luau::FileUtils::readFile(sourcemapPath.fsPath()))
454+
{
455+
workspaceFolder->client->sendTrace("Updating sourcemap contents from " + sourcemapPath.toString());
456+
return updateSourceMapFromContents(sourceMapContents.value());
457+
}
458+
else
459+
{
460+
workspaceFolder->client->sendTrace("Sourcemap file failed to read");
461+
return false;
462+
}
463+
}
464+
else if (pluginInfo)
465+
{
466+
workspaceFolder->client->sendTrace("Creating sourcemap from plugin provided information");
467+
workspaceFolder->client->sendWindowMessage(
468+
lsp::MessageType::Info, "Couldn't find " + sourcemapFileName + " for workspace '" + workspaceFolder->name +
469+
"'. Using available datamodel info from companion plugin (require paths may be missing)");
470+
return updateSourceMapFromContents("{\"name\":\"Default\",\"className\":\"DataModel\",\"children\":[]}");
456471
}
457472
else
458473
{
459-
workspaceFolder->client->sendTrace("Sourcemap file failed to read");
474+
workspaceFolder->client->sendTrace("No sourcemap file or plugin information found, cannot update sourcemap");
460475
return false;
461476
}
462477
}

src/platform/roblox/RobloxStudioPlugin.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ void RobloxPlatform::onStudioPluginFullChange(const json& dataModel)
2626
workspaceFolder->client->sendLogMessage(lsp::MessageType::Info, "received full change from studio plugin");
2727

2828
pluginNodeAllocator.clear();
29-
pluginInfo = PluginNode::fromJson(dataModel, pluginNodeAllocator);
29+
setPluginInfo(PluginNode::fromJson(dataModel, pluginNodeAllocator));
3030

3131
// Mutate the sourcemap with the new information
3232
updateSourceMap();
@@ -38,7 +38,7 @@ void RobloxPlatform::onStudioPluginClear()
3838

3939
// TODO: properly handle multi-workspace setup
4040
pluginNodeAllocator.clear();
41-
pluginInfo = nullptr;
41+
setPluginInfo(nullptr);
4242

4343
// Mutate the sourcemap with the new information
4444
updateSourceMap();

tests/Sourcemap.test.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "Fixture.h"
33
#include "Platform/RobloxPlatform.hpp"
44
#include "ScopedFlags.h"
5+
#include "LuauFileUtils.hpp"
56

67
TEST_SUITE_BEGIN("SourcemapTests");
78

@@ -761,4 +762,46 @@ TEST_CASE_FIXTURE(Fixture, "child_properties_of_game_are_cleared_when_an_invalid
761762
CHECK_EQ(Luau::get<Luau::UnknownProperty>(result2.errors[0])->key, "Part");
762763
}
763764

765+
TEST_CASE_FIXTURE(Fixture, "sourcemap_update_uses_plugin_info_if_sourcemap_file_is_missing")
766+
{
767+
client->globalConfig.diagnostics.strictDatamodelTypes = true;
768+
client->globalConfig.sourcemap.enabled = true;
769+
770+
// Verify that no sourcemap file exists - this ensures we're testing the fallback behavior
771+
auto config = client->getConfiguration(workspace.rootUri);
772+
auto sourcemapPath = workspace.rootUri.resolvePath(config.sourcemap.sourcemapFile);
773+
REQUIRE_FALSE(Luau::FileUtils::exists(sourcemapPath.fsPath()));
774+
775+
// Set up plugin info with a Part child
776+
auto platform = dynamic_cast<RobloxPlatform*>(workspace.platform.get());
777+
auto pluginData = json::parse(R"(
778+
{
779+
"Name": "game",
780+
"ClassName": "DataModel",
781+
"Children": [
782+
{
783+
"Name": "TestPart",
784+
"ClassName": "Part"
785+
}
786+
]
787+
}
788+
)");
789+
platform->setPluginInfo(PluginNode::fromJson(pluginData, platform->pluginNodeAllocator));
790+
791+
// Update sourcemap successfully
792+
REQUIRE(platform->updateSourceMap());
793+
794+
// Verify the plugin info was used
795+
REQUIRE(platform->rootSourceNode);
796+
CHECK_EQ(platform->rootSourceNode->className, "DataModel");
797+
798+
auto result = check(R"(
799+
--!strict
800+
local part = game.TestPart
801+
)");
802+
803+
LUAU_LSP_REQUIRE_NO_ERRORS(result);
804+
CHECK(Luau::toString(requireType("part")) == "Part");
805+
}
806+
764807
TEST_SUITE_END();

0 commit comments

Comments
 (0)