Handle formatting of parts of formatted interpolated string

This commit is contained in:
Kampfkarren 2022-11-15 13:50:52 -08:00
parent 2d94ef6f55
commit 971088a08a
3 changed files with 47 additions and 2 deletions

View file

@ -1,6 +1,7 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details // This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Autocomplete.h" #include "Luau/Autocomplete.h"
#include "Luau/Ast.h"
#include "Luau/AstQuery.h" #include "Luau/AstQuery.h"
#include "Luau/BuiltinDefinitions.h" #include "Luau/BuiltinDefinitions.h"
#include "Luau/Frontend.h" #include "Luau/Frontend.h"
@ -1238,6 +1239,12 @@ static bool stringPartOfInterpString(const AstNode* node, Position position)
return true; return true;
} }
static bool isSimpleInterpolatedString(const AstNode* node)
{
const AstExprInterpString* interpString = node->as<AstExprInterpString>();
return interpString != nullptr && interpString->expressions.size == 0;
}
static std::optional<AutocompleteEntryMap> autocompleteStringParams(const SourceModule& sourceModule, const ModulePtr& module, static std::optional<AutocompleteEntryMap> autocompleteStringParams(const SourceModule& sourceModule, const ModulePtr& module,
const std::vector<AstNode*>& nodes, Position position, StringCompletionCallback callback) const std::vector<AstNode*>& nodes, Position position, StringCompletionCallback callback)
{ {
@ -1246,7 +1253,8 @@ static std::optional<AutocompleteEntryMap> autocompleteStringParams(const Source
return std::nullopt; return std::nullopt;
} }
if (!nodes.back()->is<AstExprConstantString>() && !stringPartOfInterpString(nodes.back(), position) && !nodes.back()->is<AstExprError>()) if (!nodes.back()->is<AstExprConstantString>() && !isSimpleInterpolatedString(nodes.back()) && !nodes.back()->is<AstExprInterpString>() &&
!nodes.back()->is<AstExprError>())
{ {
return std::nullopt; return std::nullopt;
} }
@ -1490,7 +1498,7 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
{ {
return {*ret, ancestry, AutocompleteContext::String}; return {*ret, ancestry, AutocompleteContext::String};
} }
else if (node->is<AstExprConstantString>() || stringPartOfInterpString(node, position)) else if (node->is<AstExprConstantString>() || isSimpleInterpolatedString(node))
{ {
AutocompleteEntryMap result; AutocompleteEntryMap result;
@ -1516,6 +1524,13 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
return {result, ancestry, AutocompleteContext::String}; return {result, ancestry, AutocompleteContext::String};
} }
else if (stringPartOfInterpString(node, position))
{
// We're not a simple interpolated string, we're something like `a{"b"}@1`, and we
// can't know what to format to
AutocompleteEntryMap map;
return {map, ancestry, AutocompleteContext::String};
}
if (node->is<AstExprConstantNumber>()) if (node->is<AstExprConstantNumber>())
return {}; return {};

View file

@ -2692,17 +2692,21 @@ AstExpr* Parser::parseInterpString()
break; break;
} }
bool errorWhileChecking = false;
switch (lexer.current().type) switch (lexer.current().type)
{ {
case Lexeme::InterpStringMid: case Lexeme::InterpStringMid:
case Lexeme::InterpStringEnd: case Lexeme::InterpStringEnd:
{ {
errorWhileChecking = true;
nextLexeme(); nextLexeme();
expressions.push_back(reportExprError(endLocation, {}, "Malformed interpolated string, expected expression inside '{}'")); expressions.push_back(reportExprError(endLocation, {}, "Malformed interpolated string, expected expression inside '{}'"));
break; break;
} }
case Lexeme::BrokenString: case Lexeme::BrokenString:
{ {
errorWhileChecking = true;
nextLexeme(); nextLexeme();
expressions.push_back(reportExprError(endLocation, {}, "Malformed interpolated string, did you forget to add a '`'?")); expressions.push_back(reportExprError(endLocation, {}, "Malformed interpolated string, did you forget to add a '`'?"));
break; break;
@ -2711,6 +2715,11 @@ AstExpr* Parser::parseInterpString()
expressions.push_back(parseExpr()); expressions.push_back(parseExpr());
} }
if (errorWhileChecking)
{
break;
}
switch (lexer.current().type) switch (lexer.current().type)
{ {
case Lexeme::InterpStringBegin: case Lexeme::InterpStringBegin:

View file

@ -2761,6 +2761,27 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_interpolated_string_expression_with_c
CHECK_EQ(ac.context, AutocompleteContext::Expression); CHECK_EQ(ac.context, AutocompleteContext::Expression);
} }
TEST_CASE_FIXTURE(ACFixture, "autocomplete_interpolated_string_as_singleton")
{
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
check(R"(
--!strict
local function f(a: "cat" | "dog") end
f(`@1`)
f(`uhhh{'try'}@2`)
)");
auto ac = autocomplete('1');
CHECK(ac.entryMap.count("cat"));
CHECK_EQ(ac.context, AutocompleteContext::String);
ac = autocomplete('2');
CHECK(ac.entryMap.empty());
CHECK_EQ(ac.context, AutocompleteContext::String);
}
TEST_CASE_FIXTURE(ACFixture, "autocomplete_explicit_type_pack") TEST_CASE_FIXTURE(ACFixture, "autocomplete_explicit_type_pack")
{ {
check(R"( check(R"(