Skip to content

Commit

Permalink
Allow completion of children variables with string literals when needed
Browse files Browse the repository at this point in the history
  • Loading branch information
4ian committed Nov 30, 2023
1 parent d4a3c60 commit ad56b29
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 13 deletions.
15 changes: 9 additions & 6 deletions Core/GDCore/Events/Parsers/ExpressionParser2NodePrinter.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <memory>
#include <vector>

#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
namespace gd {
Expand Down Expand Up @@ -43,6 +44,11 @@ class GD_CORE_API ExpressionParser2NodePrinter
*/
const gd::String& GetOutput() { return output; };

static gd::String PrintStringLiteral(const gd::String& str) {
return "\"" +
str.FindAndReplace("\\", "\\\\").FindAndReplace("\"", "\\\"") + "\"";
}

protected:
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
output += "(";
Expand All @@ -69,10 +75,7 @@ class GD_CORE_API ExpressionParser2NodePrinter
}
void OnVisitNumberNode(NumberNode& node) override { output += node.number; }
void OnVisitTextNode(TextNode& node) override {
output +=
"\"" +
node.text.FindAndReplace("\\", "\\\\").FindAndReplace("\"", "\\\"") +
"\"";
output += PrintStringLiteral(node.text);
}
void OnVisitVariableNode(VariableNode& node) override {
output += node.name;
Expand All @@ -97,8 +100,8 @@ class GD_CORE_API ExpressionParser2NodePrinter
}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
if (!node.behaviorFunctionName.empty()) {
output +=
node.objectName + "." + node.objectFunctionOrBehaviorName + "::" + node.behaviorFunctionName;
output += node.objectName + "." + node.objectFunctionOrBehaviorName +
"::" + node.behaviorFunctionName;
} else {
output += node.objectName + "." + node.objectFunctionOrBehaviorName;
}
Expand Down
15 changes: 11 additions & 4 deletions Core/GDCore/IDE/Events/ExpressionCompletionFinder.h
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,8 @@ class GD_CORE_API ExpressionCompletionFinder
} else {
// Object function, behavior name, variable, object variable.
if (IsCaretOn(node.identifierNameLocation)) {
// Don't attempt to complete children variables if there is
// already a dot written (`MyVariable.`).
bool eagerlyCompleteIfPossible =
!node.identifierNameDotLocation.IsValid();
AddCompletionsForAllIdentifiersMatchingSearch(
Expand Down Expand Up @@ -833,8 +835,8 @@ class GD_CORE_API ExpressionCompletionFinder
}

/**
* A slightly less strict check than `gd::Project::IsNameSafe` as child variables can be completed
* even if they start with a number.
* A slightly less strict check than `gd::Project::IsNameSafe` as child
* variables can be completed even if they start with a number.
*/
bool IsIdentifierSafe(const gd::String& name) {
if (name.empty()) return false;
Expand Down Expand Up @@ -898,14 +900,19 @@ class GD_CORE_API ExpressionCompletionFinder
if (variable.GetType() == gd::Variable::Structure) {
gd::String prefix = variableName + ".";
for (const auto& name : variable.GetAllChildrenNames()) {
if (!IsIdentifierSafe(name)) continue;
gd::String completion =
IsIdentifierSafe(name)
? (prefix + name)
: (variableName + "[" +
gd::ExpressionParser2NodePrinter::PrintStringLiteral(name) +
"]");

const auto& childVariable = variable.GetChild(name);
ExpressionCompletionDescription description(
ExpressionCompletionDescription::Variable,
location.GetStartPosition(),
location.GetEndPosition());
description.SetCompletion(prefix + name);
description.SetCompletion(completion);
description.SetVariableType(childVariable.GetType());
completions.push_back(description);
}
Expand Down
1 change: 0 additions & 1 deletion Core/GDCore/IDE/Events/ExpressionVariableParentFinder.h
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,6 @@ class GD_CORE_API ExpressionVariableParentFinder

const gd::String& firstChildName = *childVariableNames.begin();

// TODO: move again?
const gd::Variable* variable = variablesContainer.Has(firstChildName) ?
&variablesContainer.Get(firstChildName) : nullptr;
if (childVariableNames.size() == 1 || !variable)
Expand Down
6 changes: 5 additions & 1 deletion GDevelop.js/__tests__/ExpressionCompletionFinder.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ describe('gd.ExpressionCompletionFinder', function () {
structureChild1.getChild('Child1StructureChild2');
structureChild1.getChild('Child1StructureChild3');
structureChild1.getChild(
'Child1 Unsafe Because of Spaces so not listed'
'Child1Structure with unsafe "`/+ characters and spaces'
);

// With a child array, containing 2 structures and a number.
Expand Down Expand Up @@ -272,6 +272,7 @@ describe('gd.ExpressionCompletionFinder', function () {
).toMatchInlineSnapshot(`
[
"{ 3, no type, 3, no prefix, Child1Structure, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
"{ 3, no type, 1, no prefix, Child1Structure["Child1Structure with unsafe \\"\`/+ characters and spaces"], no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
"{ 3, no type, 1, no prefix, Child1Structure.Child1StructureChild1, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
"{ 3, no type, 1, no prefix, Child1Structure.Child1StructureChild2, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
"{ 3, no type, 1, no prefix, Child1Structure.Child1StructureChild3, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
Expand Down Expand Up @@ -317,6 +318,7 @@ describe('gd.ExpressionCompletionFinder', function () {
).toMatchInlineSnapshot(`
[
"{ 3, no type, 3, no prefix, Child1Structure, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
"{ 3, no type, 1, no prefix, Child1Structure["Child1Structure with unsafe \\"\`/+ characters and spaces"], no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
"{ 3, no type, 1, no prefix, Child1Structure.Child1StructureChild1, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
"{ 3, no type, 1, no prefix, Child1Structure.Child1StructureChild2, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
"{ 3, no type, 1, no prefix, Child1Structure.Child1StructureChild3, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
Expand Down Expand Up @@ -829,6 +831,7 @@ describe('gd.ExpressionCompletionFinder', function () {
).toMatchInlineSnapshot(`
[
"{ 3, no type, 3, no prefix, Child1Structure, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
"{ 3, no type, 1, no prefix, Child1Structure["Child1Structure with unsafe \\"\`/+ characters and spaces"], no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
"{ 3, no type, 1, no prefix, Child1Structure.Child1StructureChild1, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
"{ 3, no type, 1, no prefix, Child1Structure.Child1StructureChild2, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
"{ 3, no type, 1, no prefix, Child1Structure.Child1StructureChild3, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
Expand Down Expand Up @@ -865,6 +868,7 @@ describe('gd.ExpressionCompletionFinder', function () {
).toMatchInlineSnapshot(`
[
"{ 3, no type, 3, no prefix, Child1Structure, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
"{ 3, no type, 1, no prefix, Child1Structure["Child1Structure with unsafe \\"\`/+ characters and spaces"], no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
"{ 3, no type, 1, no prefix, Child1Structure.Child1StructureChild1, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
"{ 3, no type, 1, no prefix, Child1Structure.Child1StructureChild2, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
"{ 3, no type, 1, no prefix, Child1Structure.Child1StructureChild3, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ const AutocompletionRow = React.forwardRef(
|},
ref
) => {
const trimmedLabel = label.length > 46 ? label.substr(0, 46) + '…' : label;

return (
<ButtonBase
style={styles.button}
Expand All @@ -112,7 +114,7 @@ const AutocompletionRow = React.forwardRef(
{icon || (iconSrc ? <AutocompletionIcon src={iconSrc} /> : null)}
<Spacer />
<Text style={defaultTextStyle} noMargin align="left">
{isSelected ? <b>{label}</b> : label}
{isSelected ? <b>{trimmedLabel}</b> : trimmedLabel}
{parametersLabel && (
<>
(<i>{parametersLabel}</i>)
Expand Down

0 comments on commit ad56b29

Please sign in to comment.