From 86494918f51a1cd6290c2616c508bd36cc34b5a3 Mon Sep 17 00:00:00 2001 From: JohnnyMorganz Date: Wed, 11 Jan 2023 16:28:11 +0000 Subject: [PATCH] Pass string content to autocomplete callback (#800) Closes #718 We pass an extra `contents` parameter to the string callback function to provide contextual information for autocomplete. Because we support both string literals and simple interpolated strings, we pass it through as a `std::string` value. This is a breaking change --- Analysis/include/Luau/Autocomplete.h | 3 ++- Analysis/src/Autocomplete.cpp | 24 ++++++++++++++++++-- tests/Autocomplete.test.cpp | 33 +++++++++++++++++++++++++--- 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/Analysis/include/Luau/Autocomplete.h b/Analysis/include/Luau/Autocomplete.h index a4101e16..61832577 100644 --- a/Analysis/include/Luau/Autocomplete.h +++ b/Analysis/include/Luau/Autocomplete.h @@ -89,7 +89,8 @@ struct AutocompleteResult }; using ModuleName = std::string; -using StringCompletionCallback = std::function(std::string tag, std::optional ctx)>; +using StringCompletionCallback = + std::function(std::string tag, std::optional ctx, std::optional contents)>; AutocompleteResult autocomplete(Frontend& frontend, const ModuleName& moduleName, Position position, StringCompletionCallback callback); diff --git a/Analysis/src/Autocomplete.cpp b/Analysis/src/Autocomplete.cpp index 49c430e6..9f046b8e 100644 --- a/Analysis/src/Autocomplete.cpp +++ b/Analysis/src/Autocomplete.cpp @@ -1262,6 +1262,23 @@ static bool isSimpleInterpolatedString(const AstNode* node) return interpString != nullptr && interpString->expressions.size == 0; } +static std::optional getStringContents(const AstNode* node) +{ + if (const AstExprConstantString* string = node->as()) + { + return std::string(string->value.data, string->value.size); + } + else if (const AstExprInterpString* interpString = node->as(); interpString && interpString->expressions.size == 0) + { + LUAU_ASSERT(interpString->strings.size == 1); + return std::string(interpString->strings.data->data, interpString->strings.data->size); + } + else + { + return std::nullopt; + } +} + static std::optional autocompleteStringParams(const SourceModule& sourceModule, const ModulePtr& module, const std::vector& nodes, Position position, StringCompletionCallback callback) { @@ -1294,10 +1311,13 @@ static std::optional autocompleteStringParams(const Source return std::nullopt; } - auto performCallback = [&](const FunctionType* funcType) -> std::optional { + std::optional candidateString = getStringContents(nodes.back()); + + auto performCallback = [&](const FunctionType* funcType) -> std::optional + { for (const std::string& tag : funcType->tags) { - if (std::optional ret = callback(tag, getMethodContainingClass(module, candidate->func))) + if (std::optional ret = callback(tag, getMethodContainingClass(module, candidate->func), candidateString)) { return ret; } diff --git a/tests/Autocomplete.test.cpp b/tests/Autocomplete.test.cpp index f241963a..0ac86e82 100644 --- a/tests/Autocomplete.test.cpp +++ b/tests/Autocomplete.test.cpp @@ -18,7 +18,7 @@ LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel) using namespace Luau; -static std::optional nullCallback(std::string tag, std::optional ptr) +static std::optional nullCallback(std::string tag, std::optional ptr, std::optional contents) { return std::nullopt; } @@ -36,9 +36,9 @@ struct ACFixtureImpl : BaseType return Luau::autocomplete(this->frontend, "MainModule", Position{row, column}, nullCallback); } - AutocompleteResult autocomplete(char marker) + AutocompleteResult autocomplete(char marker, StringCompletionCallback callback = nullCallback) { - return Luau::autocomplete(this->frontend, "MainModule", getPosition(marker), nullCallback); + return Luau::autocomplete(this->frontend, "MainModule", getPosition(marker), callback); } CheckResult check(const std::string& source) @@ -3363,4 +3363,31 @@ TEST_CASE_FIXTURE(ACFixture, "type_reduction_is_hooked_up_to_autocomplete") // CHECK("{| x: nil |}" == toString(*ty2, opts)); } +TEST_CASE_FIXTURE(ACFixture, "string_contents_is_available_to_callback") +{ + loadDefinition(R"( + declare function require(path: string): any + )"); + + std::optional require = frontend.typeCheckerForAutocomplete.globalScope->linearSearchForBinding("require"); + REQUIRE(require); + Luau::unfreeze(frontend.typeCheckerForAutocomplete.globalTypes); + attachTag(require->typeId, "RequireCall"); + Luau::freeze(frontend.typeCheckerForAutocomplete.globalTypes); + + check(R"( + local x = require("testing/@1") + )"); + + bool isCorrect = false; + auto ac1 = autocomplete('1', + [&isCorrect](std::string, std::optional, std::optional contents) -> std::optional + { + isCorrect = contents.has_value() && contents.value() == "testing/"; + return std::nullopt; + }); + + CHECK(isCorrect); +} + TEST_SUITE_END();