qmlls: auto completion for inside for each statements

Save the source location of the parenthesis and the `in` or `of` token.
With these locations, provide support for for..in and for..of
statements.

Task-number: QTBUG-117445
Change-Id: I642e252caa5e8fc9152bcd233d14ce4680e41e32
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Sami Shalayel 2023-11-03 17:15:10 +01:00
parent 9776460553
commit aada41a2f1
4 changed files with 114 additions and 1 deletions

View File

@ -2157,6 +2157,9 @@ void QQmlDomAstCreator::endVisit(AST::ForEachStatement *exp)
return;
auto current = makeGenericScriptElement(exp, DomType::ScriptForEachStatement);
current->addLocation(FileLocationRegion::InOfTokenRegion, exp->inOfToken);
current->addLocation(FileLocationRegion::LeftParenthesisRegion, exp->lparenToken);
current->addLocation(FileLocationRegion::RightParenthesisRegion, exp->rparenToken);
if (exp->statement) {
Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty());

View File

@ -2757,6 +2757,33 @@ static QList<CompletionItem> insideDoWhileStatement(const DomItem &currentItem,
return {};
}
static QList<CompletionItem> insideForEachStatement(const DomItem &currentItem,
const CompletionContextStrings &ctx)
{
const auto regions = FileLocations::treeOf(currentItem)->info().regions;
const QQmlJS::SourceLocation inOf = regions[InOfTokenRegion];
const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
if (betweenLocations(leftParenthesis, ctx, inOf)) {
QList<CompletionItem> res;
res << QQmlLSUtils::scriptIdentifierCompletion(currentItem, ctx)
<< QQmlLSUtils::suggestVariableDeclarationStatementCompletion();
return res;
}
if (betweenLocations(inOf, ctx, rightParenthesis)) {
const QList<CompletionItem> res = QQmlLSUtils::scriptIdentifierCompletion(currentItem, ctx);
return res;
}
if (afterLocation(rightParenthesis, ctx)) {
return QQmlLSUtils::suggestJSStatementCompletion(currentItem);
}
return {};
}
QList<CompletionItem> QQmlLSUtils::completions(const DomItem &currentItem,
const CompletionContextStrings &ctx)
{
@ -2828,6 +2855,8 @@ QList<CompletionItem> QQmlLSUtils::completions(const DomItem &currentItem,
return insideWhileStatement(currentParent, ctx);
case DomType::ScriptDoWhileStatement:
return insideDoWhileStatement(currentParent, ctx);
case DomType::ScriptForEachStatement:
return insideForEachStatement(currentParent, ctx);
// TODO: Implement those statements.
// In the meanwhile, suppress completions to avoid weird behaviors.
@ -2844,7 +2873,6 @@ QList<CompletionItem> QQmlLSUtils::completions(const DomItem &currentItem,
case DomType::ScriptCaseClauses:
case DomType::ScriptCaseClause:
case DomType::ScriptDefaultClause:
case DomType::ScriptForEachStatement:
return {};
default:

View File

@ -130,4 +130,8 @@ Zzz {
function helloDoWhileStatement(hello) {
do --hello; while (hello);
}
function helloForEachStatement(hello) {
for(variable in hello) ++hello;
for(element of hello) ++hello;
}
}

View File

@ -2440,6 +2440,84 @@ void tst_qmlls_utils::completions_data()
<< ExpectedCompletions{ { u"hello"_s, CompletionItemKind::Variable },
{ letStatementCompletion, CompletionItemKind::Snippet } }
<< QStringList{ propertyCompletion } << None;
QTest::newRow("forInStatementLet")
<< file << 134 << 13
<< ExpectedCompletions{
{ letStatementCompletion, CompletionItemKind::Snippet },
{ constStatementCompletion, CompletionItemKind::Snippet },
{ varStatementCompletion, CompletionItemKind::Snippet },
{ u"helloJSStatements"_s, CompletionItemKind::Method }
}
<< QStringList{
u"property"_s,
u"readonly"_s,
u"required"_s,
forStatementCompletion,
ifStatementCompletion,
} << None;
QTest::newRow("forOfStatementLet")
<< file << 135 << 13
<< ExpectedCompletions{
{ letStatementCompletion, CompletionItemKind::Snippet },
{ constStatementCompletion, CompletionItemKind::Snippet },
{ varStatementCompletion, CompletionItemKind::Snippet },
{ u"helloJSStatements"_s, CompletionItemKind::Method }
}
<< QStringList{
u"property"_s,
u"readonly"_s,
u"required"_s,
forStatementCompletion,
ifStatementCompletion,
} << None;
QTest::newRow("forInStatementTarget")
<< file << 134 << 25
<< ExpectedCompletions{
{ u"helloJSStatements"_s, CompletionItemKind::Method },
{ u"hello"_s, CompletionItemKind::Variable },
}
<< QStringList{ u"property"_s, u"readonly"_s, u"required"_s,
forStatementCompletion, ifStatementCompletion, varStatementCompletion,
letStatementCompletion, constStatementCompletion, }
<< None;
QTest::newRow("forOfStatementTarget")
<< file << 135 << 24
<< ExpectedCompletions{
{ u"helloJSStatements"_s, CompletionItemKind::Method },
{ u"hello"_s, CompletionItemKind::Variable },
}
<< QStringList{ u"property"_s, u"readonly"_s, u"required"_s,
forStatementCompletion, ifStatementCompletion, varStatementCompletion,
letStatementCompletion, constStatementCompletion, }
<< None;
QTest::newRow("forInStatementConsequence")
<< file << 134 << 31
<< ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
{ constStatementCompletion, CompletionItemKind:: Snippet },
{ varStatementCompletion, CompletionItemKind::Snippet },
{ u"helloJSStatements"_s, CompletionItemKind::Method },
{ u"hello"_s, CompletionItemKind::Variable },
{ forStatementCompletion, CompletionItemKind::Snippet }
}
<< QStringList{ propertyCompletion }
<< None;
QTest::newRow("forOfStatementConsequence")
<< file << 135 << 33
<< ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
{ constStatementCompletion, CompletionItemKind:: Snippet },
{ varStatementCompletion, CompletionItemKind::Snippet },
{ u"helloJSStatements"_s, CompletionItemKind::Method },
{ u"hello"_s, CompletionItemKind::Variable },
{ forStatementCompletion, CompletionItemKind::Snippet }
}
<< QStringList{ propertyCompletion }
<< None;
}
void tst_qmlls_utils::completions()