Skip to content

Commit

Permalink
Handle positional constructor parameters properly
Browse files Browse the repository at this point in the history
  • Loading branch information
FirentisTFW committed Nov 15, 2024
1 parent c5a3340 commit 91611a5
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 45 deletions.
16 changes: 14 additions & 2 deletions src/data/dart_class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class DartClass {
class DartClassConstructor {
constructor(
public named: boolean,
public fields: Array<DartClassField>,
public fields: Array<DartClassConstructorField>,
public name: string | null
) {}

Expand All @@ -26,4 +26,16 @@ class DartClassField {
) {}
}

export { DartClass, DartClassConstructor, DartClassField };
class DartClassConstructorField extends DartClassField {
constructor(
public name: string,
public type: string,
public nullable = false,
public named = true,
) {
super(name, type, nullable);
}
}

export { DartClass, DartClassConstructor, DartClassConstructorField, DartClassField };

6 changes: 5 additions & 1 deletion src/generators/file_content/impl/3_0_0_generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,11 @@ class FileContentGenerator3_0_0 extends BaseFileContentGenerator {
`;

for (const field of constructor.fields) {
output += `${field.name}: ${this.knobForField(field)},\n`;
if (field.named) {
output += `${field.name}: ${this.knobForField(field)},\n`;
} else {
output += `${this.knobForField(field)},\n`;
}
}

output += `
Expand Down
111 changes: 87 additions & 24 deletions src/test/dart_class_parser.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DartClassConstructor, DartClassField } from "../data/dart_class";
import { DartClassConstructor, DartClassConstructorField, DartClassField } from "../data/dart_class";
import {
doesLookingFurtherMakeSense,
isConstructorLine,
Expand Down Expand Up @@ -435,11 +435,11 @@ describe("parseLinesToConstructor", () => {
" })",
];
const classFields = [
new DartClassField("active", "bool"),
new DartClassField("semanticsLabel", "String"),
new DartClassConstructorField("active", "bool"),
new DartClassConstructorField("semanticsLabel", "String"),
// class can have more fields which are not in the main constructor part because they are in its initializer list
new DartClassField("text", "String"),
new DartClassField("icon", "IconData"),
new DartClassConstructorField("text", "String"),
new DartClassConstructorField("icon", "IconData"),
];

expect(parseLinesToConstructor(lines, "Loader", classFields)).toEqual(
Expand All @@ -453,8 +453,8 @@ describe("parseLinesToConstructor", () => {
// " const Loader({super.key, this.active = true, this.semanticsLabel});",
// ];
// const classFields = [
// new DartClassField("active", "bool"),
// new DartClassField("semanticsLabel", "String"),
// new DartClassConstructorField("active", "bool"),
// new DartClassConstructorField("semanticsLabel", "String"),
// ];

// expect(parseLinesToConstructor(lines, "Loader", classFields)).toEqual(
Expand All @@ -473,10 +473,10 @@ describe("parseLinesToConstructor", () => {
" })",
];
const classFields = [
new DartClassField("active", "bool"),
new DartClassField("semanticsLabel", "String"),
new DartClassField("text", "String"),
new DartClassField("icon", "IconData"),
new DartClassConstructorField("active", "bool"),
new DartClassConstructorField("semanticsLabel", "String"),
new DartClassConstructorField("text", "String"),
new DartClassConstructorField("icon", "IconData"),
];

expect(parseLinesToConstructor(lines, "Loader", classFields)).toEqual(
Expand All @@ -485,8 +485,39 @@ describe("parseLinesToConstructor", () => {
[
classFields[0],
classFields[1],
new DartClassField("big", "bool"),
new DartClassField("animated", "bool"),
new DartClassConstructorField("big", "bool"),
new DartClassConstructorField("animated", "bool"),
],
null
)
);
});

test("with positional fields and named fields", () => {
const lines = [
" const Loader(",
" this.active, {",
" super.key,",
" this.semanticsLabel,",
" bool big = false,",
" required bool animated,",
" })",
];
const classFields = [
new DartClassConstructorField("active", "bool"),
new DartClassConstructorField("semanticsLabel", "String"),
new DartClassConstructorField("text", "String"),
new DartClassConstructorField("icon", "IconData"),
];

expect(parseLinesToConstructor(lines, "Loader", classFields)).toEqual(
new DartClassConstructor(
false,
[
classFields[0],
classFields[1],
new DartClassConstructorField("big", "bool"),
new DartClassConstructorField("animated", "bool"),
],
null
)
Expand All @@ -502,7 +533,7 @@ describe("parseLinesToConstructor", () => {
" required super.active,",
" })",
];
const classFields = [new DartClassField("active", "bool")];
const classFields = [new DartClassConstructorField("active", "bool")];

expect(parseLinesToConstructor(lines, "Loader", classFields)).toEqual(
new DartClassConstructor(true, [], "small")
Expand All @@ -518,11 +549,11 @@ describe("parseLinesToConstructor", () => {
" }) : big = false,",
];
const classFields = [
new DartClassField("active", "bool"),
new DartClassField("semanticsLabel", "String"),
new DartClassConstructorField("active", "bool"),
new DartClassConstructorField("semanticsLabel", "String"),
// class can have more fields which are not in the main constructor part because they are in its initializer list
new DartClassField("text", "String"),
new DartClassField("icon", "IconData"),
new DartClassConstructorField("text", "String"),
new DartClassConstructorField("icon", "IconData"),
];

expect(parseLinesToConstructor(lines, "Loader", classFields)).toEqual(
Expand All @@ -545,10 +576,42 @@ describe("parseLinesToConstructor", () => {
" })",
];
const classFields = [
new DartClassField("active", "bool"),
new DartClassField("semanticsLabel", "String"),
new DartClassField("text", "String"),
new DartClassField("icon", "IconData"),
new DartClassConstructorField("active", "bool"),
new DartClassConstructorField("semanticsLabel", "String"),
new DartClassConstructorField("text", "String"),
new DartClassConstructorField("icon", "IconData"),
];

expect(parseLinesToConstructor(lines, "Loader", classFields)).toEqual(
new DartClassConstructor(
true,
[
classFields[0],
classFields[1],
new DartClassConstructorField("big", "bool"),
new DartClassConstructorField("animated", "bool"),
],
"small"
)
);
});


test("with positional fields and named fields", () => {
const lines = [
" const Loader.small(",
" this.active, {",
" super.key,",
" this.semanticsLabel,",
" bool big = false,",
" required bool animated,",
" })",
];
const classFields = [
new DartClassConstructorField("active", "bool"),
new DartClassConstructorField("semanticsLabel", "String"),
new DartClassConstructorField("text", "String"),
new DartClassConstructorField("icon", "IconData"),
];

expect(parseLinesToConstructor(lines, "Loader", classFields)).toEqual(
Expand All @@ -557,8 +620,8 @@ describe("parseLinesToConstructor", () => {
[
classFields[0],
classFields[1],
new DartClassField("big", "bool"),
new DartClassField("animated", "bool"),
new DartClassConstructorField("big", "bool"),
new DartClassConstructorField("animated", "bool"),
],
"small"
)
Expand Down
109 changes: 91 additions & 18 deletions src/util/dart_class_parser.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
DartClass,
DartClassConstructor,
DartClassConstructorField,
DartClassField,
} from "../data/dart_class";

Expand Down Expand Up @@ -133,36 +134,40 @@ function parseLinesToConstructor(
const constructorName = isNamed
? nameLine.substring(nameLine.indexOf(".") + 1, nameLine.indexOf("("))
: null;
const namedParametersStartIndex = lines.findIndex((line) =>
line.includes("{")
);

const fieldsLines = lines
.join("")
.split(",")
// TODO Modify the line below when adding support for one-line constructors
.filter((line) => line !== "" && !line.includes("})"));
const classFieldsLines = fieldsLines.filter((line) =>
line.includes(classFieldReference)
);

const classFieldsNames = classFieldsLines.map((line) => {
const lineFromFieldName = line.substringAfter(classFieldReference);
const hasDefaultValue = lineFromFieldName.includes(" = ");
if (hasDefaultValue) {
return lineFromFieldName.substring(0, lineFromFieldName.indexOf(" = "));
}
return lineFromFieldName;
});
const positionalClassFieldsLines = fieldsLines.filter(
(line) =>
line.includes(classFieldReference) &&
fieldsLines.indexOf(line) <= namedParametersStartIndex
);
const namedClassFieldsLines = fieldsLines.filter(
(line) =>
line.includes(classFieldReference) &&
fieldsLines.indexOf(line) > namedParametersStartIndex
);

const classFields = classFieldsNames
.map((fieldName) =>
allClassFields.find((field) => field.name === fieldName)
)
.whereType<DartClassField>();
const classFields = getConstructorClassFields(
positionalClassFieldsLines,
namedClassFieldsLines,
allClassFields
);

const customFieldsLines = fieldsLines.filter(
(line) => !line.includes(classFieldReference) && !line.includes("super.")
);

const customFields = customFieldsLines.map((line) => {
const named = lines.indexOf(line) > namedParametersStartIndex;

const lineParts = line.trim().split(" ");
let name: string;
let type: string;
Expand All @@ -175,10 +180,15 @@ function parseLinesToConstructor(
}

if (type.endsWith("?")) {
return new DartClassField(name, type.removeTrailing(1), true);
return new DartClassConstructorField(
name,
type.removeTrailing(1),
true,
named
);
}

return new DartClassField(name, type);
return new DartClassConstructorField(name, type, false, named);
});

return new DartClassConstructor(
Expand All @@ -188,6 +198,69 @@ function parseLinesToConstructor(
);
}

function getConstructorClassFields(
positionalClassFieldsLines: Array<string>,
namedClassFieldsLines: Array<string>,
allClassFields: Array<DartClassField>
): Array<DartClassConstructorField> {
const classFieldReference = "this.";

const positionalClassFieldsNames = getClassFieldNames(
positionalClassFieldsLines,
classFieldReference
);
const namedClassFieldsNames = getClassFieldNames(
namedClassFieldsLines,
classFieldReference
);

const positionalClassFields = positionalClassFieldsNames
.map((fieldName) =>
allClassFields.find((field) => field.name === fieldName)
)
.whereType<DartClassField>()
.map((field) => {
return classFieldToConstructorField(field, false);
});

const namedClassFields = namedClassFieldsNames
.map((fieldName) =>
allClassFields.find((field) => field.name === fieldName)
)
.whereType<DartClassField>()
.map((field) => {
return classFieldToConstructorField(field, true);
});

return [...namedClassFields, ...positionalClassFields];
}

function classFieldToConstructorField(
classField: DartClassField,
named: boolean
): DartClassConstructorField {
return new DartClassConstructorField(
classField.name,
classField.type,
classField.nullable,
named
);
}

function getClassFieldNames(
lines: Array<string>,
classFieldReference: string
): Array<string> {
return lines.map((line) => {
const lineFromFieldName = line.substringAfter(classFieldReference);
const hasDefaultValue = lineFromFieldName.includes(" = ");
if (hasDefaultValue) {
return lineFromFieldName.substring(0, lineFromFieldName.indexOf(" = "));
}
return lineFromFieldName;
});
}

function doesLookingFurtherMakeSense(line: string): boolean {
return (
!line.includes("Widget build(BuildContext context)") &&
Expand Down

0 comments on commit 91611a5

Please sign in to comment.