Skip to content

Commit 1000845

Browse files
committed
misc: Ensure there is exactly one blank line before and after output
In the case where a generator returns output without a trailing newline, it would end up right next to the next generator header
1 parent cc5eff5 commit 1000845

File tree

5 files changed

+85
-21
lines changed

5 files changed

+85
-21
lines changed

source_gen/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
* Simplification to the output of generator names in header sections.
44

5+
* Update handling of whitespace in generator outputs.
6+
* If the output from a generator has wrapping whitespace, it is trimmed.
7+
* If the output from a generator is empty or whitespace-only, it is ignored.
8+
* These changes will likely have no effect on output, unless you customize
9+
the code formatter.
10+
511
## 0.8.1+3
612

713
* More redundant new lines elimination.

source_gen/lib/src/builder.dart

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ class _Builder extends Builder {
115115
LineSplitter.split(item.toString()).map((line) => '// $line\n'))
116116
..writeln(_headerLine)
117117
..writeln()
118-
..write(item.output);
118+
..writeln(item.output);
119119
}
120120

121121
var genPartContent = contentBuffer.toString();
@@ -212,9 +212,16 @@ Stream<GeneratedOutput> _generate(LibraryElement library,
212212
log.fine(msg);
213213
var createdUnit = await gen.generate(libraryReader, buildStep);
214214

215-
if (createdUnit != null && createdUnit.isNotEmpty) {
216-
yield new GeneratedOutput(gen, createdUnit);
215+
if (createdUnit == null) {
216+
continue;
217217
}
218+
219+
createdUnit = createdUnit.trim();
220+
if (createdUnit.isEmpty) {
221+
continue;
222+
}
223+
224+
yield new GeneratedOutput(gen, createdUnit);
218225
} catch (e, stack) {
219226
log.severe('Error running $gen', e, stack);
220227
yield new GeneratedOutput.fromError(gen, e, stack);

source_gen/lib/src/generated_output.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ class GeneratedOutput {
1717
GeneratedOutput(this.generator, this.output)
1818
: error = null,
1919
stackTrace = null,
20-
assert(output != null && output.isNotEmpty);
20+
assert(output != null),
21+
assert(output.isNotEmpty),
22+
// assuming length check is cheaper than simple string equality
23+
assert(output.length == output.trim().length);
2124

2225
GeneratedOutput.fromError(this.generator, this.error, this.stackTrace)
2326
: this.output = _outputFromError(error);

source_gen/test/builder_test.dart

Lines changed: 61 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ void main() {
6565
test('Expect no error when multiple generators used on nonstandalone builder',
6666
() async {
6767
expect(
68-
() =>
69-
new PartBuilder([const CommentGenerator(), const _NoOpGenerator()]),
68+
() => new PartBuilder(
69+
[const CommentGenerator(), const _LiteralGenerator()]),
7070
returnsNormally);
7171
});
7272

@@ -103,9 +103,9 @@ void main() {
103103
const CommentGenerator(forClasses: true, forLibrary: true),
104104
_testGenPartContentForClassesAndLibrary));
105105

106-
test('No-op generator produces no generated parts', () async {
106+
test('null result produces no generated parts', () async {
107107
var srcs = _createPackageStub();
108-
var builder = new PartBuilder([const _NoOpGenerator()]);
108+
var builder = _unformattedLiteral();
109109
await testBuilder(builder, srcs, outputs: {});
110110
});
111111

@@ -133,6 +133,38 @@ void main() {
133133
expect(logs, ['Missing "part \'test_lib.g.dart\';".']);
134134
});
135135

136+
test('generator with an empty result creates no outputs', () async {
137+
var srcs = _createPackageStub(testLibContent: _testLibContentNoPart);
138+
var builder = _unformattedLiteral('');
139+
await testBuilder(
140+
builder,
141+
srcs,
142+
outputs: {},
143+
);
144+
});
145+
146+
test('generator with whitespace-only result has no outputs', () async {
147+
var srcs = _createPackageStub(testLibContent: _testLibContentNoPart);
148+
var builder = _unformattedLiteral('\n \n');
149+
await testBuilder(
150+
builder,
151+
srcs,
152+
outputs: {},
153+
);
154+
});
155+
156+
test('generator result with wrapping whitespace is trimmed', () async {
157+
var srcs = _createPackageStub(testLibContent: _testLibContent);
158+
var builder = _unformattedLiteral('\n// hello\n');
159+
await testBuilder(
160+
builder,
161+
srcs,
162+
outputs: {
163+
'$_pkgName|lib/test_lib.g.dart': _whitespaceTrimmed,
164+
},
165+
);
166+
});
167+
136168
test('defaults to formatting generated code with the DartFormatter',
137169
() async {
138170
await testBuilder(new PartBuilder([const UnformattedCodeGenerator()]),
@@ -191,15 +223,15 @@ void main() {
191223
});
192224

193225
test('Should have a readable toString() message for builders', () {
194-
final builder = new LibraryBuilder(const _NoOpGenerator());
195-
expect(builder.toString(), 'Generating .g.dart: _NoOpGenerator');
226+
final builder = new LibraryBuilder(const _LiteralGenerator());
227+
expect(builder.toString(), 'Generating .g.dart: _LiteralGenerator');
196228

197229
final builders = new PartBuilder([
198-
const _NoOpGenerator(),
199-
const _NoOpGenerator(),
230+
const _LiteralGenerator(),
231+
const _LiteralGenerator(),
200232
]);
201233
expect(builders.toString(),
202-
'Generating .g.dart: _NoOpGenerator, _NoOpGenerator');
234+
'Generating .g.dart: _LiteralGenerator, _LiteralGenerator');
203235
});
204236
}
205237

@@ -223,12 +255,17 @@ Map<String, String> _createPackageStub(
223255
};
224256
}
225257

226-
/// Doesn't generate output for any element
227-
class _NoOpGenerator extends Generator {
228-
const _NoOpGenerator();
258+
PartBuilder _unformattedLiteral([String content]) =>
259+
new PartBuilder([new _LiteralGenerator(content)], formatOutput: (s) => s);
260+
261+
/// Returns the [String] provided in the constructor, or `null`.
262+
class _LiteralGenerator extends Generator {
263+
final String _content;
264+
265+
const _LiteralGenerator([this._content]);
229266

230267
@override
231-
Future<String> generate(LibraryReader library, _) => null;
268+
FutureOr<String> generate(_, __) => _content;
232269
}
233270

234271
class _BadOutputGenerator extends Generator {
@@ -341,3 +378,14 @@ part of 'test_lib.dart';
341378
342379
// Code for "class A"
343380
''';
381+
382+
const _whitespaceTrimmed = r'''// GENERATED CODE - DO NOT MODIFY BY HAND
383+
384+
part of test_lib;
385+
386+
// **************************************************************************
387+
// _LiteralGenerator
388+
// **************************************************************************
389+
390+
// hello
391+
''';

source_gen/test/src/comment_generator.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ class CommentGenerator extends Generator {
1515

1616
@override
1717
Future<String> generate(LibraryReader library, _) async {
18-
var output = new StringBuffer();
18+
var output = <String>[];
1919
if (forLibrary) {
2020
var name = library.element.name;
2121
if (name.isEmpty) {
2222
name = library.element.source.uri.pathSegments.last;
2323
}
24-
output.writeln('// Code for "$name"');
24+
output.add('// Code for "$name"');
2525
}
2626
if (forClasses) {
2727
for (var classElement
@@ -32,9 +32,9 @@ class CommentGenerator extends Generator {
3232
todo: 'Rename ${classElement.displayName} to something else.',
3333
element: classElement);
3434
}
35-
output.writeln('// Code for "$classElement"');
35+
output.add('// Code for "$classElement"');
3636
}
3737
}
38-
return '$output';
38+
return output.join('\n');
3939
}
4040
}

0 commit comments

Comments
 (0)