Skip to content

Commit

Permalink
完美生成routes.g.dart sync代码,待async
Browse files Browse the repository at this point in the history
  • Loading branch information
chen56 committed Apr 29, 2024
1 parent 9d9e67b commit 529b1d0
Show file tree
Hide file tree
Showing 3 changed files with 334 additions and 25 deletions.
321 changes: 321 additions & 0 deletions packages/you_cli/lib/src/code_builder_ext.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,321 @@

import 'package:code_builder/code_builder.dart';
import 'package:path/path.dart' as path;
import 'package:you_cli/src/urils.dart';

/// 自定义Allocator: 更清晰干净的前缀
class CleanPrefixedAllocator implements Allocator {
final _importWithSymbols = <String, Set<String>>{};
final _importsUrlAndAs = <String, String?>{};
final _asKeys = <String,int>{};

@override
String allocate(Reference reference) {
String symbol = reference.symbol!;
String? url = reference.url;

if (url == null) {
return symbol;
}

Set<String>? symbols = _importWithSymbols[url];

if (symbols == null) {
// init
_importWithSymbols[url] = symbols = {};
}

for (var MapEntry(key: otherUrl, value: symbols) in _importWithSymbols.entries) {
if (otherUrl == url) {
continue;
}
if (symbols.contains(symbol)) {
// 冲突
String as = _importsUrlAndAs.putIfAbsent(url, () {
Uri uri = Uri.parse(url);
String filename = path.basename(uri.path).replaceFirst(".dart", "");
var dirname=path.basename(path.dirname(uri.path));
var as = paths.pathPartToVar("_${dirname}_$filename");
int times = _asKeys.putIfAbsent(as, ()=>0);
if(times==0){
_asKeys[as]=++times;
return as;
}else{
_asKeys[as]=++times;
return "$as$times";
}
})!;
return "$as.$symbol";
}
}

// 未发现冲突
symbols.add(symbol);
_importsUrlAndAs.putIfAbsent(url, () => null);
return symbol;
}

@override
Iterable<Directive> get imports => _importsUrlAndAs.keys.map(
(u) => Directive.import(u, as: _importsUrlAndAs[u]),
);
}

/// copy from code_builder source code
/// 【you_cli】update code:
/// - visitInvokeExpression : only remove last arguments: ','
/// - visitClass : only remove field newline ','
/// - visitMixin : only remove field newline ','
/// - visitLiteralListExpression: only add ',' at last
class DartEmitterForCli extends DartEmitter {
bool _withInConstExpression = false;

DartEmitterForCli({super.allocator, super.orderDirectives, super.useNullSafetySyntax});

@override
StringSink visitInvokeExpression(InvokeExpression expression, [StringSink? output]) {
final out = output ??= StringBuffer();
return _writeConstExpression(out, expression.isConst, () {
expression.target.accept(this, out);
if (expression.name != null) {
out
..write('.')
..write(expression.name);
}
if (expression.typeArguments.isNotEmpty) {
out.write('<');
visitAll<Reference>(expression.typeArguments, out, (type) {
type.accept(this, out);
});
out.write('>');
}
out.write('(');
visitAll<Spec>(expression.positionalArguments, out, (spec) {
spec.accept(this, out);
});
if (expression.positionalArguments.isNotEmpty && expression.namedArguments.isNotEmpty) {
out.write(', ');
}
visitAll<String>(expression.namedArguments.keys, out, (name) {
out
..write(name)
..write(': ');
expression.namedArguments[name]!.accept(this, out);
});
final argumentCount = expression.positionalArguments.length + expression.namedArguments.length;
if (argumentCount > 1) {
// ***【you_cli】: only change this code:***
// out.write(', ');
}
return out..write(')');
});
}

@override
StringSink visitLiteralListExpression(
LiteralListExpression expression, [
StringSink? output,
]) {
final out = output ??= StringBuffer();

return _writeConstExpression(output, expression.isConst, () {
if (expression.type != null) {
out.write('<');
expression.type!.accept(this, output);
out.write('>');
}
out.write('[');
visitAll<Object?>(expression.values, out, (value) {
_acceptLiteral(value, out);
});
// ***【you_cli】: only change this code:***
// old: if (expression.values.length > 1) {
/*new*/ if (expression.values.isNotEmpty) {
out.write(', ');
}
return out..write(']');
});
}
void _acceptLiteral(Object? literalOrSpec, StringSink output) {
if (literalOrSpec is Spec) {
literalOrSpec.accept(this, output);
return;
}
literal(literalOrSpec).accept(this, output);
}

@override
StringSink visitClass(Class spec, [StringSink? output]) {
final out = output ??= StringBuffer();
spec.docs.forEach(out.writeln);
for (var a in spec.annotations) {
visitAnnotation(a, out);
}

void writeModifier() {
if (spec.modifier != null) {
out.write('${spec.modifier!.name} ');
}
}

if (spec.sealed) {
out.write('sealed ');
} else {
if (spec.abstract) {
out.write('abstract ');
}
writeModifier();
if (spec.mixin) {
out.write('mixin ');
}
}
out.write('class ${spec.name}');
visitTypeParameters(spec.types.map((r) => r.type), out);
if (spec.extend != null) {
out.write(' extends ');
spec.extend!.type.accept(this, out);
}
if (spec.mixins.isNotEmpty) {
out
..write(' with ')
..writeAll(spec.mixins.map<StringSink>((m) => m.type.accept(this)), ',');
}
if (spec.implements.isNotEmpty) {
out
..write(' implements ')
..writeAll(spec.implements.map<StringSink>((m) => m.type.accept(this)), ',');
}
out.write(' {');
for (var c in spec.constructors) {
visitConstructor(c, spec.name, out);
out.writeln();
}
for (var f in spec.fields) {
visitField(f, out);
// ***【you_cli】: only change this code:***
// out.writeln();
}
for (var m in spec.methods) {
visitMethod(m, out);
if (_isLambdaMethod(m)) {
out.writeln(';');
}
out.writeln();
}
out.writeln(' }');
return out;
}

@override
StringSink visitMixin(Mixin spec, [StringSink? output]) {
final out = output ??= StringBuffer();
spec.docs.forEach(out.writeln);
for (var a in spec.annotations) {
visitAnnotation(a, out);
}

if (spec.base) {
out.write('base ');
}
out.write('mixin ${spec.name}');
visitTypeParameters(spec.types.map((r) => r.type), out);
if (spec.on != null) {
out.write(' on ');
spec.on!.type.accept(this, out);
}
if (spec.implements.isNotEmpty) {
out
..write(' implements ')
..writeAll(spec.implements.map<StringSink>((m) => m.type.accept(this)), ',');
}
out.write(' {');
for (var f in spec.fields) {
visitField(f, out);
// ***【you_cli】: only change this code:***
// out.writeln();
}
for (var m in spec.methods) {
visitMethod(m, out);
if (_isLambdaMethod(m)) {
out.write(';');
}
out.writeln();
}
out.write(' }');
return out;
}

static bool _isLambdaBody(Code? code) => code is ToCodeExpression && !code.isStatement;

/// Whether the provided [method] is considered a lambda method.
static bool _isLambdaMethod(Method method) => method.lambda ?? _isLambdaBody(method.body);

/// Executes [visit] within a context which may alter the output if [isConst]
/// is `true`.
///
/// This allows constant expressions to omit the `const` keyword if they
/// are already within a constant expression.
@override
void startConstCode(
bool isConst,
Null Function() visit,
) {
final previousConstContext = _withInConstExpression;
if (isConst) {
_withInConstExpression = true;
}

visit();
_withInConstExpression = previousConstContext;
}

/// Similar to [startConstCode], but handles writing `"const "` if [isConst]
/// is `true` and the invocation is not nested under other invocations where
/// [isConst] is true.
StringSink _writeConstExpression(
StringSink sink,
bool isConst,
StringSink Function() visitExpression,
) {
final previousConstContext = _withInConstExpression;
if (isConst) {
if (!_withInConstExpression) {
sink.write('const ');
}
_withInConstExpression = true;
}

final returnedSink = visitExpression();
assert(identical(returnedSink, sink));
_withInConstExpression = previousConstContext;
return sink;
}
}


/// Helper method improving on [StringSink.writeAll].
///
/// For every `Spec` in [elements], executing [visit].
///
/// If [elements] is at least 2 elements, inserts [separator] delimiting them.
StringSink visitAll<T>(
Iterable<T> elements,
StringSink output,
void Function(T) visit, [
String separator = ', ',
]) {
// Basically, this whole method is an improvement on
// output.writeAll(specs.map((s) => s.accept(visitor));
//
// ... which would allocate more StringBuffer(s) for a one-time use.
if (elements.isEmpty) {
return output;
}
final iterator = elements.iterator..moveNext();
visit(iterator.current);
while (iterator.moveNext()) {
output.write(separator);
visit(iterator.current);
}
return output;
}
37 changes: 12 additions & 25 deletions packages/you_cli/lib/src/urils.dart
Original file line number Diff line number Diff line change
@@ -1,28 +1,15 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/visitor.dart';

Analyzer analyzerUtils=Analyzer();

class Analyzer{
Iterable<T> findChildrenByType<T>(Element findFrom){
CollectChildElement<T> e=CollectChildElement<T>();
findFrom.accept(e);
return e.list;
// if (findFrom is T) yield findFrom as T;
// for(var child in findFrom.children){
// yield* findChildrenByType(child);
// }
}
}

class CollectChildElement<T> extends GeneralizingElementVisitor{
List<T> list=[];
@override
visitElement(Element element) {
print("ffffff$element");
if(element is T){
list.add(element as T);
}
return super.visitElement(element);
Paths paths=Paths();
class Paths{
String pathPartToVar(String pathPart){
return pathPart
// ignore: unnecessary_string_escapes
.replaceAll(RegExp("^\\d+\."), "") // 1.z.about -> note_note-self
.replaceAll(".", "_")
.replaceAll("-", "_")
.replaceAll("&", "_")
.replaceAll("*", "_")
.replaceAll("*", "_")
.replaceAll("@", "_");
}
}
1 change: 1 addition & 0 deletions packages/you_flutter/lib/src/router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ base class To {
final PageBuilder? _builder;
final PageLayoutBuilder? layout;

// TODO P1 root Node的part是routes,有问题!
To(this.part, {
PageBuilder? builder,
this.layout,
Expand Down

0 comments on commit 529b1d0

Please sign in to comment.