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

Commit 1fafa99

Browse files
authored
Cleanup: Make all ASTs lazy (no _AbstractCodeBuilder) (#12)
* Make all builders lazy, remove AST cloning. * Fix breakages due to bad refactor of FileBuilder * Small pubspec increment * Update pubspec to allow pub publish. * Add headers, upstream analyzer patch API * Address comments. * Fix outstanding lints.
1 parent 8dc8d48 commit 1fafa99

14 files changed

+234
-122
lines changed

README.md

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,35 @@
33
[![Build Status](https://travis-ci.org/dart-lang/code_builder.svg)](https://travis-ci.org/dart-lang/code_builder)
44
[![Coverage Status](https://coveralls.io/repos/dart-lang/code_builder/badge.svg)](https://coveralls.io/r/dart-lang/code_builder)
55

6-
Code builder is a fluent Dart API for generating valid Dart source code.
6+
`code_builder` is a fluent Dart API for generating valid Dart source code.
77

8-
Generally speaking, code generation usually is done through a series of
9-
string concatenation which results in messy and sometimes invalid code
10-
that is not easily readable.
8+
Code generation was traditionally done through a series of
9+
package-specific string concatenations which usually results in messy
10+
and sometimes invalid Dart code that is not easily readable and is very
11+
difficult to refactor.
1112

12-
Code builder uses the [analyzer](analyzer) package to create real Dart
13+
`code_builder` uses the [analyzer](analyzer) package to create real Dart
1314
language ASTs, which, while not guaranteed to be correct, always follows
1415
the analyzer's own understood format.
1516

1617
[analyzer]: https://pub.dartlang.org/packages/analyzer
1718

18-
Code builder also adds a more narrow and user-friendly API. For example
19-
creating a class with a method is an easy affair:
19+
## Experimental
20+
21+
While `code_builder` is considered *stable*, the APIs are subject to
22+
frequent breaking change - a number of Dart language features are not
23+
yet implemented that make it unsuitable for all forms of code
24+
generation.
25+
26+
**Contributions are [welcome][welcome]!**
27+
28+
[welcome]: CONTRIBUTING.md
29+
30+
## Usage
31+
32+
Code builder has a narrow and user-friendly API.
33+
34+
For example creating a class with a method:
2035

2136
```dart
2237
new ClassBuilder('Animal', extends: 'Organism')
@@ -32,4 +47,39 @@ class Animal extends Organism {
3247
}
3348
```
3449

35-
This package is in development and APIs are subject to frequent change.
50+
Have a complicated set of dependencies for your generated code?
51+
`code_builder` supports automatic scoping of your ASTs to automatically
52+
use prefixes to avoid symbol conflicts:
53+
54+
```dart
55+
var lib = new LibraryBuilder.scope()
56+
..addDeclaration(new MethodBuilder(
57+
name: 'doThing',
58+
returns: new TypeBuilder(
59+
'Thing',
60+
importFrom: 'package:thing/thing.dart',
61+
),
62+
))
63+
..addDeclaration(new MethodBuilder(
64+
name: 'doOtherThing',
65+
returns: new TypeBuilder(
66+
'Thing',
67+
importFrom: 'package:thing/alternative.dart',
68+
))
69+
..addParameter(new ParameterBuilder(
70+
'thing',
71+
type: new TypeBuilder(
72+
'Thing',
73+
importFrom: 'package:thing/thing.dart',
74+
),
75+
)));
76+
```
77+
78+
Outputs:
79+
```dart
80+
import 'package:thing/thing.dart' as _i1;
81+
import 'package:thing/alternative.dart' as _i2;
82+
83+
_i1.Thing doThing() {}
84+
_i2.Thing doOtherThing(_i1.Thing thing) {}
85+
```

lib/code_builder.dart

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ import 'package:analyzer/src/dart/ast/token.dart';
3434
import 'package:dart_style/dart_style.dart';
3535
import 'package:meta/meta.dart';
3636

37+
import 'src/analyzer_patch.dart';
38+
3739
part 'src/builders/annotation_builder.dart';
3840
part 'src/builders/class_builder.dart';
3941
part 'src/builders/constructor_builder.dart';
@@ -54,11 +56,6 @@ final DartFormatter _dartfmt = new DartFormatter();
5456
@visibleForTesting
5557
String dartfmt(String source) => _dartfmt.format(source);
5658

57-
// Creates a deep copy of an AST node.
58-
AstNode/*=E*/ _cloneAst/*<E extends AstNode>*/(AstNode/*=E*/ astNode) {
59-
return new AstCloner().cloneNode/*<E>*/(astNode);
60-
}
61-
6259
Identifier _stringIdentifier(String s) {
6360
return new SimpleIdentifier(new StringToken(TokenType.STRING, s, 0));
6461
}
@@ -74,17 +71,3 @@ abstract class CodeBuilder<A extends AstNode> {
7471
/// Uses [scope] to output an AST re-written to use appropriate prefixes.
7572
A toAst([Scope scope = const Scope.identity()]);
7673
}
77-
78-
@Deprecated('Builders are all becoming lazy')
79-
abstract class _AbstractCodeBuilder<A extends AstNode> extends CodeBuilder<A> {
80-
final A _astNode;
81-
82-
_AbstractCodeBuilder._(this._astNode);
83-
84-
/// Returns a copy-safe [AstNode] representing the current builder state.
85-
@override
86-
A toAst([_]) => _cloneAst/*<A>*/(_astNode);
87-
88-
@override
89-
String toString() => '$runtimeType: ${_astNode.toSource()}';
90-
}

lib/src/analyzer_patch.dart

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright (c) 2016, 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 'package:analyzer/src/generated/java_core.dart';
6+
7+
/// Implements both old-API [PrintWriter] and new-API [StringBuffer].
8+
///
9+
/// This makes it easier to re-use our `pretty_printer` until analyzer updates.
10+
class PrintBuffer implements PrintWriter, StringBuffer {
11+
final StringBuffer _impl = new StringBuffer();
12+
13+
@override
14+
void clear() {}
15+
16+
@override
17+
bool get isEmpty => _impl.isEmpty;
18+
19+
@override
20+
bool get isNotEmpty => _impl.isNotEmpty;
21+
22+
@override
23+
int get length => _impl.length;
24+
25+
@override
26+
void newLine() {
27+
_impl.writeln();
28+
}
29+
30+
@override
31+
void print(x) {
32+
_impl.write(x);
33+
}
34+
35+
@override
36+
void printf(String fmt, List args) => throw new UnimplementedError();
37+
38+
@override
39+
void println(String s) {
40+
_impl.writeln(s);
41+
}
42+
43+
@override
44+
void write(Object obj) {
45+
_impl.write(obj);
46+
}
47+
48+
@override
49+
void writeAll(Iterable objects, [String separator = ""]) {
50+
_impl.writeAll(objects);
51+
}
52+
53+
@override
54+
void writeCharCode(int charCode) {
55+
_impl.writeCharCode(charCode);
56+
}
57+
58+
@override
59+
void writeln([Object obj = ""]) {
60+
_impl.writeln(obj);
61+
}
62+
63+
@override
64+
String toString() => _impl.toString();
65+
}

lib/src/builders/class_builder.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class ClassBuilder implements CodeBuilder<ClassDeclaration> {
2525
/// Create a new builder for a `class` named [name].
2626
///
2727
/// Optionally, define another class to [extend] or classes to either
28-
/// [implement] or [mixin]. You may also define a `class` as [abstract].
28+
/// [implement] or [mixin].
2929
factory ClassBuilder(
3030
String name, {
3131
TypeBuilder extend,
@@ -40,6 +40,7 @@ class ClassBuilder implements CodeBuilder<ClassDeclaration> {
4040
new List<TypeBuilder>.unmodifiable(mixin),
4141
);
4242

43+
/// Create a new builder for an `abstract class` named [name].
4344
factory ClassBuilder.asAbstract(String name,
4445
{TypeBuilder extend,
4546
Iterable<TypeBuilder> implement: const [],

lib/src/builders/constructor_builder.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,21 @@ class ConstructorBuilder implements CodeBuilder<ConstructorDeclaration> {
1515
final String _name;
1616
final List<ParameterBuilder> _parameters = <ParameterBuilder>[];
1717

18+
/// Create a new builder for a constructor, optionally with a [name].
1819
factory ConstructorBuilder([String name]) {
1920
return new ConstructorBuilder._(false, name);
2021
}
2122

23+
/// Create a new builder for a constructor, optionally with a [name].
24+
///
25+
/// The resulting constructor will be `const`.
2226
factory ConstructorBuilder.isConst([String name]) {
2327
return new ConstructorBuilder._(true, name);
2428
}
2529

2630
ConstructorBuilder._(this._isConstant, this._name);
2731

28-
/// Lazily adds [parameter].
32+
/// Lazily adds [builder].
2933
///
3034
/// When the method is emitted as an AST, [ParameterBuilder.toAst] is used.
3135
void addParameter(ParameterBuilder builder) {

lib/src/builders/expression_builder.dart

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ ExpressionBuilder _invokeSelfImpl(
7676
abstract class ExpressionBuilder implements CodeBuilder<Expression> {
7777
/// Invoke [name] (which should be available in the local scope).
7878
///
79-
/// Optionally specify [positional] and [named] arguments.
79+
/// May specify [positional] and [named] arguments.
8080
factory ExpressionBuilder.invoke(
8181
String name, {
8282
String importFrom,
@@ -91,6 +91,9 @@ abstract class ExpressionBuilder implements CodeBuilder<Expression> {
9191
);
9292
}
9393

94+
/// Invoke the 'new' operator on [type].
95+
///
96+
/// May use a [name] of a constructor and [positional] and [named] arguments.
9497
factory ExpressionBuilder.invokeNew(
9598
TypeBuilder type, {
9699
String name,
@@ -247,7 +250,7 @@ class _InvokeExpression extends ExpressionBuilder {
247250
}
248251

249252
@override
250-
StatementBuilder toStatement() => new StatementBuilder.fromExpression(this);
253+
StatementBuilder toStatement() => new _ExpressionStatementBuilder(this);
251254

252255
ArgumentList _getArgumentList(Scope scope) {
253256
return new ArgumentList(
@@ -289,7 +292,7 @@ abstract class _LiteralExpression<A extends Literal>
289292
_asFunctionExpression(this, scope);
290293

291294
@override
292-
StatementBuilder toStatement() => new StatementBuilder.fromExpression(this);
295+
StatementBuilder toStatement() => new _ExpressionStatementBuilder(this);
293296
}
294297

295298
class _LiteralNull extends _LiteralExpression<NullLiteral> {

0 commit comments

Comments
 (0)