| 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 |