Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
# so this line is commented out by default.
#.vscode/

*.g.dart
*.morphy.dart
*.morphy2.dart

Expand All @@ -32,7 +33,7 @@
.packages
.pub-cache/
.pub/
/build/
*/build/
.fvm/

# Flutter macos
Expand All @@ -55,4 +56,5 @@ app.*.map.json
/android/app/release

# Ignore pub lock files
pubspec.lock
pubspec.lock

6 changes: 3 additions & 3 deletions example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ dependencies:
dev_dependencies:
morphy:
path: ../morphy
build_runner: ^2.7.0
test: ^1.25.0
json_serializable: ^6.9.4
build_runner: ^2.8.0
test: ^1.26.3
json_serializable: ^6.11.1
json_annotation: ^4.9.0


109 changes: 80 additions & 29 deletions morphy/lib/src/MorphyGenerator.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import 'dart:async';

import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';

////import 'package:analyzer_models/analyzer_models.dart';
import 'package:build/src/builder/build_step.dart';
import 'package:build/src/build_step.dart';
import 'package:dartx/dartx.dart';
import 'package:morphy/src/common/GeneratorForAnnotationX.dart';
import 'package:morphy/src/common/NameType.dart';
Expand All @@ -29,38 +29,49 @@ import 'package:source_gen/source_gen.dart';
// return (classFields + superTypeFields).distinctBy((x) => x.name).toList();
//}

class MorphyGenerator<TValueT extends MorphyX> extends GeneratorForAnnotationX<TValueT> {
class MorphyGenerator<TValueT extends MorphyX>
extends GeneratorForAnnotationX<TValueT> {
@override
FutureOr<String> generateForAnnotatedElement(Element2 ce, ConstantReader annotation, BuildStep buildStep, List<ClassElement2> allClasses) {
FutureOr<String> generateForAnnotatedElement(
Element ce,
ConstantReader annotation,
BuildStep buildStep,
List<ClassElement> allClasses,
) {
var sb = StringBuffer();

// sb.writeln("//RULES: you must use implements, not extends");

if (ce is! ClassElement2) {
if (ce is! ClassElement) {
throw Exception("not a class");
}

var hasConstConstructor = ce.constructors2.any((e) => e.isConst);
var hasConstConstructor = ce.constructors.any((e) => e.isConst);

if (ce.supertype?.element3.name3 != "Object") {
if (ce.supertype?.element.name != "Object") {
throw Exception("you must use implements, not extends");
}

var docComment = ce.documentationComment;

var isAbstract = ce.name3!.startsWith("\$\$");
var allFields = getAllFields(ce.allSupertypes, ce).where((x) => x.name != "hashCode").toList();
var isAbstract = ce.name!.startsWith("\$\$");
var allFields = getAllFields(
ce.allSupertypes,
ce,
).where((x) => x.name != "hashCode").toList();

var className = ce.name3!;
var className = ce.name!;
var interfaces = ce.interfaces
.map(
(e) => //
InterfaceWithComment(
e.element3.name3!, //
e.element.name!, //
e.typeArguments.map((e) => e.toString()).toList(),
e.element3.typeParameters2.map((x) => x.name3!).toList(),
e.element3.fields2.map((e) => NameType(e.name3!, e.type.toString())).toList(),
comment: e.element3.documentationComment,
e.element.typeParameters.map((x) => x.name!).toList(),
e.element.fields
.map((e) => NameType(e.name!, e.type.toString()))
.toList(),
comment: e.element.documentationComment,
),
) //
.toList();
Expand All @@ -69,8 +80,14 @@ class MorphyGenerator<TValueT extends MorphyX> extends GeneratorForAnnotationX<T
// sb.writeln("//interfacefields: ${element.fields.toString()})");
// });

var classGenerics = ce.typeParameters2
.map((e) => NameTypeClassComment(e.name3!, e.bound == null ? null : e.bound.toString(), null)) //
var classGenerics = ce.typeParameters
.map(
(e) => NameTypeClassComment(
e.name3!,
e.bound == null ? null : e.bound.toString(),
null,
),
) //
.toList();

var allFieldsDistinct = getDistinctFields(allFields, interfaces);
Expand All @@ -82,16 +99,26 @@ class MorphyGenerator<TValueT extends MorphyX> extends GeneratorForAnnotationX<T
.read('explicitSubTypes') //
.listValue
.map((x) {
if (x.toTypeValue()?.element3 is! Element2) {
throw Exception("each type for the copywith def must all be classes");
if (x.toTypeValue()?.element is! Element) {
throw Exception(
"each type for the copywith def must all be classes",
);
}

var el = x.toTypeValue()!.element3!;
var el = x.toTypeValue()!.element!;

return Interface.fromGenerics(
(el as InterfaceElement2).name3!, // or .name
(el as TypeParameterizedElement2).typeParameters2.map((TypeParameterElement2 x) => NameType(x.name3!, x.bound?.getDisplayString())).toList(),
getAllFields(el.allSupertypes, el).where((x) => x.name != 'hashCode').toList(),
(el as InterfaceElement).name!, // or .name
(el as TypeParameterizedElement).typeParameters
.map(
(TypeParameterElement x) =>
NameType(x.name!, x.bound?.getDisplayString()),
)
.toList(),
getAllFields(
el.allSupertypes,
el,
).where((x) => x.name != 'hashCode').toList(),
true,
);
})
Expand All @@ -104,23 +131,47 @@ class MorphyGenerator<TValueT extends MorphyX> extends GeneratorForAnnotationX<T
// Walk implemented interfaces (and their interfaces)
flatten<InterfaceType>(ce.interfaces, (x) => x.interfaces)
// Only keep real classes; avoids casts on mixins/extension types.
.where((t) => t.element3 is ClassElement2)
.where((t) => t.element is ClassElement)
.map((t) {
final cls = t.element3 as ClassElement2; // also a TypeParameterizedElement2
final tparams = (cls as TypeParameterizedElement2).typeParameters2;
final cls =
t.element as ClassElement; // also a TypeParameterizedElement2
final tparams = (cls as TypeParameterizedElement).typeParameters;

return Interface.fromGenerics(
cls.name3!, // class name
tparams.map((TypeParameterElement2 p) => NameType(p.name3!, p.bound?.getDisplayString())).toList(),
getAllFields(cls.allSupertypes, cls).where((x) => x.name != 'hashCode').toList(),
cls.name!, // class name
tparams
.map(
(TypeParameterElement p) =>
NameType(p.name!, p.bound?.getDisplayString()),
)
.toList(),
getAllFields(
cls.allSupertypes,
cls,
).where((x) => x.name != 'hashCode').toList(),
);
})
.union(typesExplicit)
.distinctBy((e) => e.interfaceName)
.toList();
;

sb.writeln(createMorphy(isAbstract, allFieldsDistinct, className, docComment ?? "", interfaces, allValueTInterfaces, classGenerics, hasConstConstructor, annotation.read('generateJson').boolValue, annotation.read('hidePublicConstructor').boolValue, typesExplicit, annotation.read('nonSealed').boolValue));
sb.writeln(
createMorphy(
isAbstract,
allFieldsDistinct,
className,
docComment ?? "",
interfaces,
allValueTInterfaces,
classGenerics,
hasConstConstructor,
annotation.read('generateJson').boolValue,
annotation.read('hidePublicConstructor').boolValue,
typesExplicit,
annotation.read('nonSealed').boolValue,
),
);

// sb.writeln(createCopyWith(classDef, otherClasses2).replaceAll("\$", ""));

Expand Down
8 changes: 4 additions & 4 deletions morphy/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ environment:
sdk: ">=3.8.0 <4.0.0"

dependencies:
analyzer: ">=7.5.9 <8.0.0"
build: ^3.0.0
source_gen: ^3.0.0
analyzer: ^8.2.0
build: ^4.0.0
source_gen: ^4.0.1
morphy_annotation: ^1.4.2
dartx: ^1.2.0

dev_dependencies:
test: ^1.25.0
test: ^1.26.3

# morphy_annotation: ^1.4.2

Expand Down
6 changes: 3 additions & 3 deletions morphy_annotation/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ environment:
sdk: ">=3.8.0 <4.0.0"

dependencies:
collection: ^1.15.0
collection: ^1.19.1
dartx: ^1.2.0
json_annotation: ^4.8.0
json_annotation: ^4.9.0
quiver: ^3.2.2

dev_dependencies:
test: ^1.25.0
test: ^1.26.3