OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 /// Code transform for @observable. The core transformation is relatively | 5 /// Code transform for @observable. The core transformation is relatively |
6 /// straightforward, and essentially like an editor refactoring. | 6 /// straightforward, and essentially like an editor refactoring. |
7 library observe.transformer; | 7 library observe.transformer; |
8 | 8 |
9 import 'dart:async'; | 9 import 'dart:async'; |
10 | 10 |
11 import 'package:analyzer/analyzer.dart'; | 11 import 'package:analyzer/analyzer.dart'; |
| 12 import 'package:analyzer/dart/ast/token.dart'; |
12 import 'package:analyzer/src/generated/ast.dart'; | 13 import 'package:analyzer/src/generated/ast.dart'; |
13 import 'package:analyzer/src/generated/scanner.dart'; | |
14 import 'package:barback/barback.dart'; | 14 import 'package:barback/barback.dart'; |
15 import 'package:code_transformers/messages/build_logger.dart'; | 15 import 'package:code_transformers/messages/build_logger.dart'; |
16 import 'package:source_maps/refactor.dart'; | 16 import 'package:source_maps/refactor.dart'; |
17 import 'package:source_span/source_span.dart'; | 17 import 'package:source_span/source_span.dart'; |
18 | 18 |
19 import 'src/messages.dart'; | 19 import 'src/messages.dart'; |
20 | 20 |
21 /// A [Transformer] that replaces observables based on dirty-checking with an | 21 /// A [Transformer] that replaces observables based on dirty-checking with an |
22 /// implementation based on change notifications. | 22 /// implementation based on change notifications. |
23 /// | 23 /// |
24 /// The transformation adds hooks for field setters and notifies the observation | 24 /// The transformation adds hooks for field setters and notifies the observation |
25 /// system of the change. | 25 /// system of the change. |
26 class ObservableTransformer extends Transformer { | 26 class ObservableTransformer extends Transformer { |
27 final bool releaseMode; | 27 final bool releaseMode; |
28 final bool injectBuildLogsInOutput; | 28 final bool injectBuildLogsInOutput; |
29 final List<String> _files; | 29 final List<String> _files; |
| 30 |
30 ObservableTransformer( | 31 ObservableTransformer( |
31 {List<String> files, bool releaseMode, bool injectBuildLogsInOutput}) | 32 {List<String> files, bool releaseMode, bool injectBuildLogsInOutput}) |
32 : _files = files, | 33 : _files = files, |
33 releaseMode = releaseMode == true, | 34 releaseMode = releaseMode == true, |
34 injectBuildLogsInOutput = injectBuildLogsInOutput == null | 35 injectBuildLogsInOutput = injectBuildLogsInOutput == null |
35 ? releaseMode != true | 36 ? releaseMode != true |
36 : injectBuildLogsInOutput; | 37 : injectBuildLogsInOutput; |
| 38 |
37 ObservableTransformer.asPlugin(BarbackSettings settings) | 39 ObservableTransformer.asPlugin(BarbackSettings settings) |
38 : _files = _readFiles(settings.configuration['files']), | 40 : _files = _readFiles(settings.configuration['files']), |
39 releaseMode = settings.mode == BarbackMode.RELEASE, | 41 releaseMode = settings.mode == BarbackMode.RELEASE, |
40 injectBuildLogsInOutput = settings.mode != BarbackMode.RELEASE; | 42 injectBuildLogsInOutput = settings.mode != BarbackMode.RELEASE; |
41 | 43 |
42 static List<String> _readFiles(value) { | 44 static List<String> _readFiles(value) { |
43 if (value == null) return null; | 45 if (value == null) return null; |
44 var files = []; | 46 var files = <String>[]; |
45 bool error; | 47 bool error; |
46 if (value is List) { | 48 if (value is List) { |
47 files = value; | 49 files = new List<String>.from(value); |
48 error = value.any((e) => e is! String); | 50 error = value.any((e) => e is! String); |
49 } else if (value is String) { | 51 } else if (value is String) { |
50 files = [value]; | 52 files = [value]; |
51 error = false; | 53 error = false; |
52 } else { | 54 } else { |
53 error = true; | 55 error = true; |
54 } | 56 } |
55 if (error) print('Invalid value for "files" in the observe transformer.'); | 57 if (error) print('Invalid value for "files" in the observe transformer.'); |
56 return files; | 58 return files; |
57 } | 59 } |
58 | 60 |
59 // TODO(nweiz): This should just take an AssetId when barback <0.13.0 support | 61 // TODO(nweiz): This should just take an AssetId when barback <0.13.0 support |
60 // is dropped. | 62 // is dropped. |
61 Future<bool> isPrimary(idOrAsset) { | 63 Future<bool> isPrimary(Object idOrAsset) { |
62 var id = idOrAsset is AssetId ? idOrAsset : idOrAsset.id; | 64 var id = idOrAsset is AssetId ? idOrAsset : (idOrAsset as Asset).id; |
63 return new Future.value(id.extension == '.dart' && | 65 return new Future.value(id.extension == '.dart' && |
64 (_files == null || _files.contains(id.path))); | 66 (_files == null || _files.contains(id.path))); |
65 } | 67 } |
66 | 68 |
67 Future apply(Transform transform) { | 69 Future apply(Transform transform) { |
68 return transform.primaryInput.readAsString().then((content) { | 70 return transform.primaryInput.readAsString().then((content) { |
69 // Do a quick string check to determine if this is this file even | 71 // Do a quick string check to determine if this is this file even |
70 // plausibly might need to be transformed. If not, we can avoid an | 72 // plausibly might need to be transformed. If not, we can avoid an |
71 // expensive parse. | 73 // expensive parse. |
72 if (!observableMatcher.hasMatch(content)) return null; | 74 if (!observableMatcher.hasMatch(content)) return null; |
(...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
249 var params = cls.typeParameters; | 251 var params = cls.typeParameters; |
250 var pos = params != null ? params.end : cls.name.end; | 252 var pos = params != null ? params.end : cls.name.end; |
251 code.edit(pos, pos, ' extends ChangeNotifier '); | 253 code.edit(pos, pos, ' extends ChangeNotifier '); |
252 } | 254 } |
253 } | 255 } |
254 | 256 |
255 SimpleIdentifier _getSimpleIdentifier(Identifier id) => | 257 SimpleIdentifier _getSimpleIdentifier(Identifier id) => |
256 id is PrefixedIdentifier ? id.identifier : id; | 258 id is PrefixedIdentifier ? id.identifier : id; |
257 | 259 |
258 bool _hasKeyword(Token token, Keyword keyword) => | 260 bool _hasKeyword(Token token, Keyword keyword) => |
259 token is KeywordToken && token.keyword == keyword; | 261 token?.type == TokenType.KEYWORD && token.lexeme == keyword.syntax; |
260 | 262 |
261 String _getOriginalCode(TextEditTransaction code, AstNode node) => | 263 String _getOriginalCode(TextEditTransaction code, AstNode node) => |
262 code.original.substring(node.offset, node.end); | 264 code.original.substring(node.offset, node.end); |
263 | 265 |
264 void _fixConstructor(ConstructorDeclaration ctor, TextEditTransaction code, | 266 void _fixConstructor(ConstructorDeclaration ctor, TextEditTransaction code, |
265 Set<String> changedFields) { | 267 Set<String> changedFields) { |
266 // Fix normal initializers | 268 // Fix normal initializers |
267 for (var initializer in ctor.initializers) { | 269 for (var initializer in ctor.initializers) { |
268 if (initializer is ConstructorFieldInitializer) { | 270 if (initializer is ConstructorFieldInitializer) { |
269 var field = initializer.fieldName; | 271 var field = initializer.fieldName; |
270 if (changedFields.contains(field.name)) { | 272 if (changedFields.contains(field.name)) { |
271 code.edit(field.offset, field.end, '__\$${field.name}'); | 273 code.edit(field.offset, field.end, '__\$${field.name}'); |
272 } | 274 } |
273 } | 275 } |
274 } | 276 } |
275 | 277 |
276 // Fix "this." initializer in parameter list. These are tricky: | 278 // Fix "this." initializer in parameter list. These are tricky: |
277 // we need to preserve the name and add an initializer. | 279 // we need to preserve the name and add an initializer. |
278 // Preserving the name is important for named args, and for dartdoc. | 280 // Preserving the name is important for named args, and for dartdoc. |
279 // BEFORE: Foo(this.bar, this.baz) { ... } | 281 // BEFORE: Foo(this.bar, this.baz) { ... } |
280 // AFTER: Foo(bar, baz) : __$bar = bar, __$baz = baz { ... } | 282 // AFTER: Foo(bar, baz) : __$bar = bar, __$baz = baz { ... } |
281 | 283 |
282 var thisInit = []; | 284 var thisInit = []; |
283 for (var param in ctor.parameters.parameters) { | 285 for (var param in ctor.parameters.parameters) { |
284 if (param is DefaultFormalParameter) { | 286 if (param is DefaultFormalParameter) { |
285 param = param.parameter; | 287 param = (param as DefaultFormalParameter).parameter; |
286 } | 288 } |
287 if (param is FieldFormalParameter) { | 289 if (param is FieldFormalParameter) { |
288 var name = param.identifier.name; | 290 var name = param.identifier.name; |
289 if (changedFields.contains(name)) { | 291 if (changedFields.contains(name)) { |
290 thisInit.add(name); | 292 thisInit.add(name); |
291 // Remove "this." but keep everything else. | 293 // Remove "this." but keep everything else. |
292 code.edit(param.thisKeyword.offset, param.period.end, ''); | 294 code.edit(param.thisKeyword.offset, param.period.end, ''); |
293 } | 295 } |
294 } | 296 } |
295 } | 297 } |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
407 token = token.next; | 409 token = token.next; |
408 } | 410 } |
409 return token; | 411 return token; |
410 } | 412 } |
411 | 413 |
412 // TODO(sigmund): remove hard coded Polymer support (@published). The proper way | 414 // TODO(sigmund): remove hard coded Polymer support (@published). The proper way |
413 // to do this would be to switch to use the analyzer to resolve whether | 415 // to do this would be to switch to use the analyzer to resolve whether |
414 // annotations are subtypes of ObservableProperty. | 416 // annotations are subtypes of ObservableProperty. |
415 final observableMatcher = | 417 final observableMatcher = |
416 new RegExp("@(published|observable|PublishedProperty|ObservableProperty)"); | 418 new RegExp("@(published|observable|PublishedProperty|ObservableProperty)"); |
OLD | NEW |