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/src/generated/ast.dart'; | 12 import 'package:analyzer/src/generated/ast.dart'; |
12 import 'package:analyzer/src/generated/error.dart'; | 13 import 'package:analyzer/src/generated/error.dart'; |
13 import 'package:analyzer/src/generated/parser.dart'; | 14 import 'package:analyzer/src/generated/parser.dart'; |
14 import 'package:analyzer/src/generated/scanner.dart'; | 15 import 'package:analyzer/src/generated/scanner.dart'; |
15 import 'package:barback/barback.dart'; | 16 import 'package:barback/barback.dart'; |
16 import 'package:source_maps/refactor.dart'; | 17 import 'package:source_maps/refactor.dart'; |
17 import 'package:source_maps/span.dart' show SourceFile; | 18 import 'package:source_maps/span.dart' show SourceFile; |
18 | 19 |
19 /// A [Transformer] that replaces observables based on dirty-checking with an | 20 /// A [Transformer] that replaces observables based on dirty-checking with an |
20 /// implementation based on change notifications. | 21 /// implementation based on change notifications. |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
77 // TODO(sigmund): emit source maps when barback supports it (see | 78 // TODO(sigmund): emit source maps when barback supports it (see |
78 // dartbug.com/12340) | 79 // dartbug.com/12340) |
79 printer.build(url); | 80 printer.build(url); |
80 transform.addOutput(new Asset.fromString(id, printer.text)); | 81 transform.addOutput(new Asset.fromString(id, printer.text)); |
81 }); | 82 }); |
82 } | 83 } |
83 } | 84 } |
84 | 85 |
85 TextEditTransaction _transformCompilationUnit( | 86 TextEditTransaction _transformCompilationUnit( |
86 String inputCode, SourceFile sourceFile, TransformLogger logger) { | 87 String inputCode, SourceFile sourceFile, TransformLogger logger) { |
87 var unit = _parseCompilationUnit(inputCode); | 88 var unit = parseCompilationUnit(inputCode, suppressErrors: true); |
88 var code = new TextEditTransaction(inputCode, sourceFile); | 89 var code = new TextEditTransaction(inputCode, sourceFile); |
89 for (var directive in unit.directives) { | 90 for (var directive in unit.directives) { |
90 if (directive is LibraryDirective && _hasObservable(directive)) { | 91 if (directive is LibraryDirective && _hasObservable(directive)) { |
91 logger.warning('@observable on a library no longer has any effect. ' | 92 logger.warning('@observable on a library no longer has any effect. ' |
92 'It should be placed on individual fields.', | 93 'It should be placed on individual fields.', |
93 span: _getSpan(sourceFile, directive)); | 94 span: _getSpan(sourceFile, directive)); |
94 break; | 95 break; |
95 } | 96 } |
96 } | 97 } |
97 | 98 |
98 for (var declaration in unit.declarations) { | 99 for (var declaration in unit.declarations) { |
99 if (declaration is ClassDeclaration) { | 100 if (declaration is ClassDeclaration) { |
100 _transformClass(declaration, code, sourceFile, logger); | 101 _transformClass(declaration, code, sourceFile, logger); |
101 } else if (declaration is TopLevelVariableDeclaration) { | 102 } else if (declaration is TopLevelVariableDeclaration) { |
102 if (_hasObservable(declaration)) { | 103 if (_hasObservable(declaration)) { |
103 logger.warning('Top-level fields can no longer be observable. ' | 104 logger.warning('Top-level fields can no longer be observable. ' |
104 'Observable fields should be put in an observable objects.', | 105 'Observable fields should be put in an observable objects.', |
105 span: _getSpan(sourceFile, declaration)); | 106 span: _getSpan(sourceFile, declaration)); |
106 } | 107 } |
107 } | 108 } |
108 } | 109 } |
109 return code; | 110 return code; |
110 } | 111 } |
111 | 112 |
112 /// Parse [code] using analyzer. | |
113 CompilationUnit _parseCompilationUnit(String code) { | |
114 var errorListener = new _ErrorCollector(); | |
115 var reader = new CharSequenceReader(code); | |
116 var scanner = new Scanner(null, reader, errorListener); | |
117 var token = scanner.tokenize(); | |
118 var parser = new Parser(null, errorListener); | |
119 return parser.parseCompilationUnit(token); | |
120 } | |
121 | |
122 class _ErrorCollector extends AnalysisErrorListener { | |
123 final errors = <AnalysisError>[]; | |
124 onError(error) => errors.add(error); | |
125 } | |
126 | |
127 _getSpan(SourceFile file, AstNode node) => file.span(node.offset, node.end); | 113 _getSpan(SourceFile file, AstNode node) => file.span(node.offset, node.end); |
128 | 114 |
129 /// True if the node has the `@observable` or `@published` annotation. | 115 /// True if the node has the `@observable` or `@published` annotation. |
130 // TODO(jmesserly): it is not good to be hard coding Polymer support here. | 116 // TODO(jmesserly): it is not good to be hard coding Polymer support here. |
131 bool _hasObservable(AnnotatedNode node) => | 117 bool _hasObservable(AnnotatedNode node) => |
132 node.metadata.any(_isObservableAnnotation); | 118 node.metadata.any(_isObservableAnnotation); |
133 | 119 |
134 // TODO(jmesserly): this isn't correct if the annotation has been imported | 120 // TODO(jmesserly): this isn't correct if the annotation has been imported |
135 // with a prefix, or cases like that. We should technically be resolving, but | 121 // with a prefix, or cases like that. We should technically be resolving, but |
136 // that is expensive in analyzer, so it isn't feasible yet. | 122 // that is expensive in analyzer, so it isn't feasible yet. |
137 bool _isObservableAnnotation(Annotation node) => | 123 bool _isObservableAnnotation(Annotation node) => |
138 _isAnnotationContant(node, 'observable') || | 124 _isAnnotationContant(node, 'observable') || |
139 _isAnnotationContant(node, 'published') || | 125 _isAnnotationContant(node, 'published') || |
140 _isAnnotationType(node, 'ObservableProperty') || | 126 _isAnnotationType(node, 'ObservableProperty') || |
141 _isAnnotationType(node, 'PublishedProperty'); | 127 _isAnnotationType(node, 'PublishedProperty'); |
142 | 128 |
143 bool _isAnnotationContant(Annotation m, String name) => | 129 bool _isAnnotationContant(Annotation m, String name) => |
144 m.name.name == name && m.constructorName == null && m.arguments == null; | 130 m.name.name == name && m.constructorName == null && m.arguments == null; |
145 | 131 |
146 bool _isAnnotationType(Annotation m, String name) => m.name == name; | 132 bool _isAnnotationType(Annotation m, String name) => m.name.name == name; |
147 | 133 |
148 void _transformClass(ClassDeclaration cls, TextEditTransaction code, | 134 void _transformClass(ClassDeclaration cls, TextEditTransaction code, |
149 SourceFile file, TransformLogger logger) { | 135 SourceFile file, TransformLogger logger) { |
150 | 136 |
151 if (_hasObservable(cls)) { | 137 if (_hasObservable(cls)) { |
152 logger.warning('@observable on a class no longer has any effect. ' | 138 logger.warning('@observable on a class no longer has any effect. ' |
153 'It should be placed on individual fields.', | 139 'It should be placed on individual fields.', |
154 span: _getSpan(file, cls)); | 140 span: _getSpan(file, cls)); |
155 } | 141 } |
156 | 142 |
(...skipping 270 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
427 | 413 |
428 Token _findFieldSeperator(Token token) { | 414 Token _findFieldSeperator(Token token) { |
429 while (token != null) { | 415 while (token != null) { |
430 if (token.type == TokenType.COMMA || token.type == TokenType.SEMICOLON) { | 416 if (token.type == TokenType.COMMA || token.type == TokenType.SEMICOLON) { |
431 break; | 417 break; |
432 } | 418 } |
433 token = token.next; | 419 token = token.next; |
434 } | 420 } |
435 return token; | 421 return token; |
436 } | 422 } |
OLD | NEW |