| 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 /** | 5 /// Code transform for @observable. The core transformation is relatively |
| 6 * Code transform for @observable. The core transformation is relatively | 6 /// straightforward, and essentially like an editor refactoring. |
| 7 * straightforward, and essentially like an editor refactoring. | |
| 8 */ | |
| 9 library observe.transformer; | 7 library observe.transformer; |
| 10 | 8 |
| 11 import 'dart:async'; | 9 import 'dart:async'; |
| 12 | 10 |
| 13 import 'package:analyzer/src/generated/ast.dart'; | 11 import 'package:analyzer/src/generated/ast.dart'; |
| 14 import 'package:analyzer/src/generated/error.dart'; | 12 import 'package:analyzer/src/generated/error.dart'; |
| 15 import 'package:analyzer/src/generated/parser.dart'; | 13 import 'package:analyzer/src/generated/parser.dart'; |
| 16 import 'package:analyzer/src/generated/scanner.dart'; | 14 import 'package:analyzer/src/generated/scanner.dart'; |
| 17 import 'package:barback/barback.dart'; | 15 import 'package:barback/barback.dart'; |
| 18 import 'package:source_maps/refactor.dart'; | 16 import 'package:source_maps/refactor.dart'; |
| 19 import 'package:source_maps/span.dart' show SourceFile; | 17 import 'package:source_maps/span.dart' show SourceFile; |
| 20 | 18 |
| 21 /** | 19 /// A [Transformer] that replaces observables based on dirty-checking with an |
| 22 * A [Transformer] that replaces observables based on dirty-checking with an | 20 /// implementation based on change notifications. |
| 23 * implementation based on change notifications. | 21 /// |
| 24 * | 22 /// The transformation adds hooks for field setters and notifies the observation |
| 25 * The transformation adds hooks for field setters and notifies the observation | 23 /// system of the change. |
| 26 * system of the change. | |
| 27 */ | |
| 28 class ObservableTransformer extends Transformer { | 24 class ObservableTransformer extends Transformer { |
| 29 | 25 |
| 30 final List<String> _files; | 26 final List<String> _files; |
| 31 ObservableTransformer([List<String> files]) : _files = files; | 27 ObservableTransformer([List<String> files]) : _files = files; |
| 32 ObservableTransformer.asPlugin(BarbackSettings settings) | 28 ObservableTransformer.asPlugin(BarbackSettings settings) |
| 33 : _files = _readFiles(settings.configuration['files']); | 29 : _files = _readFiles(settings.configuration['files']); |
| 34 | 30 |
| 35 static List<String> _readFiles(value) { | 31 static List<String> _readFiles(value) { |
| 36 if (value == null) return null; | 32 if (value == null) return null; |
| 37 var files = []; | 33 var files = []; |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 105 if (_hasObservable(declaration)) { | 101 if (_hasObservable(declaration)) { |
| 106 logger.warning('Top-level fields can no longer be observable. ' | 102 logger.warning('Top-level fields can no longer be observable. ' |
| 107 'Observable fields should be put in an observable objects.', | 103 'Observable fields should be put in an observable objects.', |
| 108 span: _getSpan(sourceFile, declaration)); | 104 span: _getSpan(sourceFile, declaration)); |
| 109 } | 105 } |
| 110 } | 106 } |
| 111 } | 107 } |
| 112 return code; | 108 return code; |
| 113 } | 109 } |
| 114 | 110 |
| 115 /** Parse [code] using analyzer. */ | 111 /// Parse [code] using analyzer. |
| 116 CompilationUnit _parseCompilationUnit(String code) { | 112 CompilationUnit _parseCompilationUnit(String code) { |
| 117 var errorListener = new _ErrorCollector(); | 113 var errorListener = new _ErrorCollector(); |
| 118 var reader = new CharSequenceReader(code); | 114 var reader = new CharSequenceReader(code); |
| 119 var scanner = new Scanner(null, reader, errorListener); | 115 var scanner = new Scanner(null, reader, errorListener); |
| 120 var token = scanner.tokenize(); | 116 var token = scanner.tokenize(); |
| 121 var parser = new Parser(null, errorListener); | 117 var parser = new Parser(null, errorListener); |
| 122 return parser.parseCompilationUnit(token); | 118 return parser.parseCompilationUnit(token); |
| 123 } | 119 } |
| 124 | 120 |
| 125 class _ErrorCollector extends AnalysisErrorListener { | 121 class _ErrorCollector extends AnalysisErrorListener { |
| 126 final errors = <AnalysisError>[]; | 122 final errors = <AnalysisError>[]; |
| 127 onError(error) => errors.add(error); | 123 onError(error) => errors.add(error); |
| 128 } | 124 } |
| 129 | 125 |
| 130 _getSpan(SourceFile file, ASTNode node) => file.span(node.offset, node.end); | 126 _getSpan(SourceFile file, ASTNode node) => file.span(node.offset, node.end); |
| 131 | 127 |
| 132 /** True if the node has the `@observable` or `@published` annotation. */ | 128 /// True if the node has the `@observable` or `@published` annotation. |
| 133 // TODO(jmesserly): it is not good to be hard coding Polymer support here. | 129 // TODO(jmesserly): it is not good to be hard coding Polymer support here. |
| 134 bool _hasObservable(AnnotatedNode node) => | 130 bool _hasObservable(AnnotatedNode node) => |
| 135 node.metadata.any(_isObservableAnnotation); | 131 node.metadata.any(_isObservableAnnotation); |
| 136 | 132 |
| 137 // TODO(jmesserly): this isn't correct if the annotation has been imported | 133 // TODO(jmesserly): this isn't correct if the annotation has been imported |
| 138 // with a prefix, or cases like that. We should technically be resolving, but | 134 // with a prefix, or cases like that. We should technically be resolving, but |
| 139 // that is expensive in analyzer, so it isn't feasible yet. | 135 // that is expensive in analyzer, so it isn't feasible yet. |
| 140 bool _isObservableAnnotation(Annotation node) => | 136 bool _isObservableAnnotation(Annotation node) => |
| 141 _isAnnotationContant(node, 'observable') || | 137 _isAnnotationContant(node, 'observable') || |
| 142 _isAnnotationContant(node, 'published') || | 138 _isAnnotationContant(node, 'published') || |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 255 if (!explicitObservable) _mixinObservable(cls, code); | 251 if (!explicitObservable) _mixinObservable(cls, code); |
| 256 | 252 |
| 257 // Fix initializers, because they aren't allowed to call the setter. | 253 // Fix initializers, because they aren't allowed to call the setter. |
| 258 for (var member in cls.members) { | 254 for (var member in cls.members) { |
| 259 if (member is ConstructorDeclaration) { | 255 if (member is ConstructorDeclaration) { |
| 260 _fixConstructor(member, code, instanceFields); | 256 _fixConstructor(member, code, instanceFields); |
| 261 } | 257 } |
| 262 } | 258 } |
| 263 } | 259 } |
| 264 | 260 |
| 265 /** Adds "with ChangeNotifier" and associated implementation. */ | 261 /// Adds "with ChangeNotifier" and associated implementation. |
| 266 void _mixinObservable(ClassDeclaration cls, TextEditTransaction code) { | 262 void _mixinObservable(ClassDeclaration cls, TextEditTransaction code) { |
| 267 // Note: we need to be careful to put the with clause after extends, but | 263 // Note: we need to be careful to put the with clause after extends, but |
| 268 // before implements clause. | 264 // before implements clause. |
| 269 if (cls.withClause != null) { | 265 if (cls.withClause != null) { |
| 270 var pos = cls.withClause.end; | 266 var pos = cls.withClause.end; |
| 271 code.edit(pos, pos, ', ChangeNotifier'); | 267 code.edit(pos, pos, ', ChangeNotifier'); |
| 272 } else if (cls.extendsClause != null) { | 268 } else if (cls.extendsClause != null) { |
| 273 var pos = cls.extendsClause.end; | 269 var pos = cls.extendsClause.end; |
| 274 code.edit(pos, pos, ' with ChangeNotifier '); | 270 code.edit(pos, pos, ' with ChangeNotifier '); |
| 275 } else { | 271 } else { |
| (...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 430 | 426 |
| 431 Token _findFieldSeperator(Token token) { | 427 Token _findFieldSeperator(Token token) { |
| 432 while (token != null) { | 428 while (token != null) { |
| 433 if (token.type == TokenType.COMMA || token.type == TokenType.SEMICOLON) { | 429 if (token.type == TokenType.COMMA || token.type == TokenType.SEMICOLON) { |
| 434 break; | 430 break; |
| 435 } | 431 } |
| 436 token = token.next; | 432 token = token.next; |
| 437 } | 433 } |
| 438 return token; | 434 return token; |
| 439 } | 435 } |
| OLD | NEW |