Skip to content
This repository was archived by the owner on Apr 8, 2025. It is now read-only.

Commit ce474a2

Browse files
committed
More work towards feature parity of 1x (#116)
* WIP. * Finish up feature parity. * Dartfmt. * Address feedback. * Format with dart_style:format for now. * Fix tests.
1 parent 2dd18d7 commit ce474a2

File tree

13 files changed

+642
-23
lines changed

13 files changed

+642
-23
lines changed

lib/code_builder.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,18 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
export 'src/allocator.dart' show Allocator;
56
export 'src/base.dart' show Spec;
67
export 'src/emitter.dart' show DartEmitter;
78
export 'src/matchers.dart' show equalsDart;
89
export 'src/specs/annotation.dart' show Annotation, AnnotationBuilder;
910
export 'src/specs/class.dart' show Class, ClassBuilder;
1011
export 'src/specs/code.dart' show Code, CodeBuilder;
1112
export 'src/specs/constructor.dart' show Constructor, ConstructorBuilder;
13+
export 'src/specs/directive.dart'
14+
show Directive, DirectiveType, DirectiveBuilder;
1215
export 'src/specs/field.dart' show Field, FieldBuilder, FieldModifier;
16+
export 'src/specs/file.dart' show File, FileBuilder;
1317
export 'src/specs/method.dart'
1418
show Method, MethodBuilder, MethodType, Parameter, ParameterBuilder;
1519
export 'src/specs/reference.dart' show Reference;

lib/src/allocator.dart

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'specs/directive.dart';
6+
import 'specs/reference.dart';
7+
8+
/// Collects references and automatically allocates prefixes and imports.
9+
///
10+
/// `Allocator` takes out the manual work of deciding whether a symbol will
11+
/// clash with other imports in your generated code, or what imports are needed
12+
/// to resolve all symbols in your generated code.
13+
abstract class Allocator {
14+
/// An allocator that does not prefix symbols nor collects imports.
15+
static const Allocator none = const _NullAllocator();
16+
17+
/// Creates a new default allocator that applies no prefixing.
18+
factory Allocator() = _Allocator;
19+
20+
/// Creates a new allocator that applies naive prefixing to avoid conflicts.
21+
///
22+
/// This implementation is not optimized for any particular code generation
23+
/// style and instead takes a conservative approach of prefixing _every_
24+
/// import except references to `dart:core` (which are considered always
25+
/// imported).
26+
factory Allocator.simplePrefixing() = _PrefixedAllocator;
27+
28+
/// Returns a reference string given a [reference] object.
29+
///
30+
/// For example, a no-op implementation:
31+
/// ```dart
32+
/// allocate(const Reference('List', 'dart:core')); // Returns 'List'.
33+
/// ```
34+
///
35+
/// Where-as an implementation that prefixes imports might output:
36+
/// ```dart
37+
/// allocate(const Reference('Foo', 'package:foo')); // Returns '_i1.Foo'.
38+
/// ```
39+
String allocate(Reference reference);
40+
41+
/// All imports that have so far been added implicitly via [allocate].
42+
Iterable<Directive> get imports;
43+
}
44+
45+
class _Allocator implements Allocator {
46+
final _imports = new Set<String>();
47+
48+
@override
49+
String allocate(Reference reference) {
50+
if (reference.url != null) {
51+
_imports.add(reference.url);
52+
}
53+
return reference.symbol;
54+
}
55+
56+
@override
57+
Iterable<Directive> get imports {
58+
return _imports.map((u) => new Directive.import(u));
59+
}
60+
}
61+
62+
class _NullAllocator implements Allocator {
63+
const _NullAllocator();
64+
65+
@override
66+
String allocate(Reference reference) => reference.symbol;
67+
68+
@override
69+
Iterable<Directive> get imports => const [];
70+
}
71+
72+
class _PrefixedAllocator implements Allocator {
73+
static const _doNotPrefix = const ['dart:core'];
74+
75+
final _imports = <String, int>{};
76+
var _keys = 1;
77+
78+
@override
79+
String allocate(Reference reference) {
80+
final symbol = reference.symbol;
81+
if (reference.url == null || _doNotPrefix.contains(reference.url)) {
82+
return symbol;
83+
}
84+
return '_${_imports.putIfAbsent(reference.url, _nextKey)}.$symbol';
85+
}
86+
87+
int _nextKey() => _keys++;
88+
89+
@override
90+
Iterable<Directive> get imports {
91+
return _imports.keys.map(
92+
(u) => new Directive.import(u, as: '_${_imports[u]}'),
93+
);
94+
}
95+
}

lib/src/emitter.dart

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,30 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'allocator.dart';
6+
import 'base.dart';
57
import 'specs/annotation.dart';
68
import 'specs/class.dart';
79
import 'specs/code.dart';
810
import 'specs/constructor.dart';
11+
import 'specs/directive.dart';
912
import 'specs/field.dart';
13+
import 'specs/file.dart';
1014
import 'specs/method.dart';
1115
import 'specs/reference.dart';
1216
import 'specs/type_reference.dart';
1317
import 'visitors.dart';
1418

15-
class DartEmitter extends GeneralizingSpecVisitor<StringSink> {
16-
const DartEmitter();
19+
class DartEmitter implements SpecVisitor<StringSink> {
20+
final Allocator _allocator;
21+
22+
/// Creates a new instance of [DartEmitter].
23+
///
24+
/// May specify an [Allocator] to use for symbols, otherwise uses a no-op.
25+
const DartEmitter([this._allocator = Allocator.none]);
26+
27+
/// Creates a new instance of [DartEmitter] with a default [Allocator].
28+
factory DartEmitter.scoped() => new DartEmitter(new Allocator());
1729

1830
@override
1931
visitAnnotation(Annotation spec, [StringSink output]) {
@@ -154,6 +166,22 @@ class DartEmitter extends GeneralizingSpecVisitor<StringSink> {
154166
return output..write(code);
155167
}
156168

169+
@override
170+
visitDirective(Directive spec, [StringSink output]) {
171+
output ??= new StringBuffer();
172+
if (spec.type == DirectiveType.import) {
173+
output.write('import ');
174+
} else {
175+
output.write('export ');
176+
}
177+
output.write("'${spec.url}'");
178+
if (spec.as != null) {
179+
output.write(' as ${spec.as}');
180+
}
181+
output.write(';');
182+
return output;
183+
}
184+
157185
@override
158186
visitField(Field spec, [StringSink output]) {
159187
output ??= new StringBuffer();
@@ -188,6 +216,25 @@ class DartEmitter extends GeneralizingSpecVisitor<StringSink> {
188216
return output;
189217
}
190218

219+
@override
220+
visitFile(File spec, [StringSink output]) {
221+
output ??= new StringBuffer();
222+
// Process the body first in order to prime the allocators.
223+
final body = new StringBuffer();
224+
for (final spec in spec.body) {
225+
body.write(visitSpec(spec));
226+
}
227+
// TODO: Allow some sort of logical ordering.
228+
for (final directive in spec.directives) {
229+
visitDirective(directive, output);
230+
}
231+
for (final directive in _allocator.imports) {
232+
visitDirective(directive, output);
233+
}
234+
output.write(body);
235+
return output;
236+
}
237+
191238
@override
192239
visitMethod(Method spec, [StringSink output]) {
193240
output ??= new StringBuffer();
@@ -293,9 +340,12 @@ class DartEmitter extends GeneralizingSpecVisitor<StringSink> {
293340

294341
@override
295342
visitReference(Reference spec, [StringSink output]) {
296-
return (output ??= new StringBuffer())..write(spec.symbol);
343+
return (output ??= new StringBuffer())..write(_allocator.allocate(spec));
297344
}
298345

346+
@override
347+
visitSpec(Spec spec) => spec.accept(this);
348+
299349
@override
300350
visitType(TypeReference spec, [StringSink output]) {
301351
output ??= new StringBuffer();

lib/src/matchers.dart

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,21 @@ String _dartfmt(String source) {
2222
}
2323

2424
/// Encodes [spec] as Dart source code.
25-
String _dart(Spec spec) =>
26-
_dartfmt(spec.accept<StringSink>(const DartEmitter()).toString());
25+
String _dart(Spec spec, DartEmitter emitter) =>
26+
_dartfmt(spec.accept<StringSink>(emitter).toString()).trim();
2727

2828
/// Returns a matcher for Dart source code.
29-
Matcher equalsDart(String source) => new _EqualsDart(_dartfmt(source));
29+
Matcher equalsDart(
30+
String source, [
31+
DartEmitter emitter = const DartEmitter(),
32+
]) =>
33+
new _EqualsDart(_dartfmt(source).trim(), emitter);
3034

3135
class _EqualsDart extends Matcher {
36+
final DartEmitter _emitter;
3237
final String _source;
3338

34-
const _EqualsDart(this._source);
39+
const _EqualsDart(this._source, this._emitter);
3540

3641
@override
3742
Description describe(Description description) => description.add(_source);
@@ -40,11 +45,18 @@ class _EqualsDart extends Matcher {
4045
Description describeMismatch(
4146
covariant Spec item,
4247
Description mismatchDescription,
43-
_,
44-
__,
45-
) =>
46-
mismatchDescription.add(_dart(item));
48+
state,
49+
verbose,
50+
) {
51+
final result = _dart(item, _emitter);
52+
return equals(result).describeMismatch(
53+
_source,
54+
mismatchDescription,
55+
state,
56+
verbose,
57+
);
58+
}
4759

4860
@override
49-
bool matches(covariant Spec item, _) => _dart(item) == _source;
61+
bool matches(covariant Spec item, _) => _dart(item, _emitter) == _source;
5062
}

lib/src/specs/directive.dart

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
library code_builder.src.specs.directive;
6+
7+
import 'package:built_value/built_value.dart';
8+
import 'package:meta/meta.dart';
9+
10+
import '../base.dart';
11+
import '../visitors.dart';
12+
13+
part 'directive.g.dart';
14+
15+
@immutable
16+
abstract class Directive implements Built<Directive, DirectiveBuilder>, Spec {
17+
factory Directive([void updates(DirectiveBuilder b)]) = _$Directive;
18+
19+
factory Directive.import(
20+
String url, {
21+
String as,
22+
}) =>
23+
new Directive((builder) => builder
24+
..as = as
25+
..type = DirectiveType.import
26+
..url = url);
27+
28+
factory Directive.export(String url) => new Directive((builder) => builder
29+
..type = DirectiveType.export
30+
..url = url);
31+
32+
Directive._();
33+
34+
@nullable
35+
String get as;
36+
37+
String get url;
38+
39+
DirectiveType get type;
40+
41+
@override
42+
R accept<R>(SpecVisitor<R> visitor) => visitor.visitDirective(this);
43+
}
44+
45+
abstract class DirectiveBuilder
46+
implements Builder<Directive, DirectiveBuilder> {
47+
factory DirectiveBuilder() = _$DirectiveBuilder;
48+
49+
DirectiveBuilder._();
50+
51+
String as;
52+
53+
String url;
54+
55+
DirectiveType type;
56+
}
57+
58+
enum DirectiveType {
59+
import,
60+
export,
61+
}

0 commit comments

Comments
 (0)