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 /** |
6 * Code transform for @observable. The core transformation is relatively | 6 * Code transform for @observable. The core transformation is relatively |
7 * straightforward, and essentially like an editor refactoring. | 7 * straightforward, and essentially like an editor refactoring. |
8 */ | 8 */ |
9 library observe.transform; | 9 library observe.transform; |
10 | 10 |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
128 if (_hasObservable(cls)) { | 128 if (_hasObservable(cls)) { |
129 logger.warning('@observable on a class no longer has any effect. ' | 129 logger.warning('@observable on a class no longer has any effect. ' |
130 'It should be placed on individual fields.', | 130 'It should be placed on individual fields.', |
131 span: _getSpan(file, cls)); | 131 span: _getSpan(file, cls)); |
132 } | 132 } |
133 | 133 |
134 // We'd like to track whether observable was declared explicitly, otherwise | 134 // We'd like to track whether observable was declared explicitly, otherwise |
135 // report a warning later below. Because we don't have type analysis (only | 135 // report a warning later below. Because we don't have type analysis (only |
136 // syntactic understanding of the code), we only report warnings that are | 136 // syntactic understanding of the code), we only report warnings that are |
137 // known to be true. | 137 // known to be true. |
138 var declaresObservable = false; | 138 var explicitObservable = false; |
| 139 var implicitObservable = false; |
139 if (cls.extendsClause != null) { | 140 if (cls.extendsClause != null) { |
140 var id = _getSimpleIdentifier(cls.extendsClause.superclass.name); | 141 var id = _getSimpleIdentifier(cls.extendsClause.superclass.name); |
141 if (id.name == 'Observable') { | 142 if (id.name == 'Observable') { |
142 code.edit(id.offset, id.end, 'ChangeNotifier'); | 143 code.edit(id.offset, id.end, 'ChangeNotifier'); |
143 declaresObservable = true; | 144 explicitObservable = true; |
144 } else if (id.name == 'ChangeNotifier') { | 145 } else if (id.name == 'ChangeNotifier') { |
145 declaresObservable = true; | 146 explicitObservable = true; |
146 } else if (id.name != 'HtmlElement' && id.name != 'CustomElement' | 147 } else if (id.name != 'HtmlElement' && id.name != 'CustomElement' |
147 && id.name != 'Object') { | 148 && id.name != 'Object') { |
148 // TODO(sigmund): this is conservative, consider using type-resolution to | 149 // TODO(sigmund): this is conservative, consider using type-resolution to |
149 // improve this check. | 150 // improve this check. |
150 declaresObservable = true; | 151 implicitObservable = true; |
151 } | 152 } |
152 } | 153 } |
153 | 154 |
154 if (cls.withClause != null) { | 155 if (cls.withClause != null) { |
155 for (var type in cls.withClause.mixinTypes) { | 156 for (var type in cls.withClause.mixinTypes) { |
156 var id = _getSimpleIdentifier(type.name); | 157 var id = _getSimpleIdentifier(type.name); |
157 if (id.name == 'Observable') { | 158 if (id.name == 'Observable') { |
158 code.edit(id.offset, id.end, 'ChangeNotifier'); | 159 code.edit(id.offset, id.end, 'ChangeNotifier'); |
159 declaresObservable = true; | 160 explicitObservable = true; |
160 break; | 161 break; |
161 } else if (id.name == 'ChangeNotifier') { | 162 } else if (id.name == 'ChangeNotifier') { |
162 declaresObservable = true; | 163 explicitObservable = true; |
163 break; | 164 break; |
164 } else { | 165 } else { |
165 // TODO(sigmund): this is conservative, consider using type-resolution | 166 // TODO(sigmund): this is conservative, consider using type-resolution |
166 // to improve this check. | 167 // to improve this check. |
167 declaresObservable = true; | 168 implicitObservable = true; |
168 } | 169 } |
169 } | 170 } |
170 } | 171 } |
171 | 172 |
172 if (!declaresObservable && cls.implementsClause != null) { | 173 if (cls.implementsClause != null) { |
173 // TODO(sigmund): consider adding type-resolution to give a more precise | 174 // TODO(sigmund): consider adding type-resolution to give a more precise |
174 // answer. | 175 // answer. |
175 declaresObservable = true; | 176 implicitObservable = true; |
176 } | 177 } |
177 | 178 |
| 179 var declaresObservable = explicitObservable || implicitObservable; |
| 180 |
178 // Track fields that were transformed. | 181 // Track fields that were transformed. |
179 var instanceFields = new Set<String>(); | 182 var instanceFields = new Set<String>(); |
180 var getters = new List<String>(); | 183 var getters = new List<String>(); |
181 var setters = new List<String>(); | 184 var setters = new List<String>(); |
182 | 185 |
183 for (var member in cls.members) { | 186 for (var member in cls.members) { |
184 if (member is FieldDeclaration) { | 187 if (member is FieldDeclaration) { |
185 if (member.isStatic) { | 188 if (member.isStatic) { |
186 if (_hasObservable(member)){ | 189 if (_hasObservable(member)){ |
187 logger.warning('Static fields can no longer be observable. ' | 190 logger.warning('Static fields can no longer be observable. ' |
(...skipping 28 matching lines...) Expand all Loading... |
216 getters.add(member.name.name); | 219 getters.add(member.name.name); |
217 } else if (_hasKeyword(member.propertyKeyword, Keyword.SET)) { | 220 } else if (_hasKeyword(member.propertyKeyword, Keyword.SET)) { |
218 setters.add(member.name.name); | 221 setters.add(member.name.name); |
219 } | 222 } |
220 } | 223 } |
221 } | 224 } |
222 | 225 |
223 // If nothing was @observable, bail. | 226 // If nothing was @observable, bail. |
224 if (instanceFields.length == 0) return; | 227 if (instanceFields.length == 0) return; |
225 | 228 |
| 229 if (!explicitObservable) _mixinObservable(cls, code); |
| 230 |
226 // Fix initializers, because they aren't allowed to call the setter. | 231 // Fix initializers, because they aren't allowed to call the setter. |
227 for (var member in cls.members) { | 232 for (var member in cls.members) { |
228 if (member is ConstructorDeclaration) { | 233 if (member is ConstructorDeclaration) { |
229 _fixConstructor(member, code, instanceFields); | 234 _fixConstructor(member, code, instanceFields); |
230 } | 235 } |
231 } | 236 } |
232 } | 237 } |
233 | 238 |
| 239 /** Adds "with ChangeNotifier" and associated implementation. */ |
| 240 void _mixinObservable(ClassDeclaration cls, TextEditTransaction code) { |
| 241 // Note: we need to be careful to put the with clause after extends, but |
| 242 // before implements clause. |
| 243 if (cls.withClause != null) { |
| 244 var pos = cls.withClause.end; |
| 245 code.edit(pos, pos, ', ChangeNotifier'); |
| 246 } else if (cls.extendsClause != null) { |
| 247 var pos = cls.extendsClause.end; |
| 248 code.edit(pos, pos, ' with ChangeNotifier '); |
| 249 } else { |
| 250 var params = cls.typeParameters; |
| 251 var pos = params != null ? params.end : cls.name.end; |
| 252 code.edit(pos, pos, ' extends ChangeNotifier '); |
| 253 } |
| 254 } |
| 255 |
234 SimpleIdentifier _getSimpleIdentifier(Identifier id) => | 256 SimpleIdentifier _getSimpleIdentifier(Identifier id) => |
235 id is PrefixedIdentifier ? (id as PrefixedIdentifier).identifier : id; | 257 id is PrefixedIdentifier ? (id as PrefixedIdentifier).identifier : id; |
236 | 258 |
237 | 259 |
238 bool _hasKeyword(Token token, Keyword keyword) => | 260 bool _hasKeyword(Token token, Keyword keyword) => |
239 token is KeywordToken && (token as KeywordToken).keyword == keyword; | 261 token is KeywordToken && (token as KeywordToken).keyword == keyword; |
240 | 262 |
241 String _getOriginalCode(TextEditTransaction code, ASTNode node) => | 263 String _getOriginalCode(TextEditTransaction code, ASTNode node) => |
242 code.original.substring(node.offset, node.end); | 264 code.original.substring(node.offset, node.end); |
243 | 265 |
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
382 | 404 |
383 Token _findFieldSeperator(Token token) { | 405 Token _findFieldSeperator(Token token) { |
384 while (token != null) { | 406 while (token != null) { |
385 if (token.type == TokenType.COMMA || token.type == TokenType.SEMICOLON) { | 407 if (token.type == TokenType.COMMA || token.type == TokenType.SEMICOLON) { |
386 break; | 408 break; |
387 } | 409 } |
388 token = token.next; | 410 token = token.next; |
389 } | 411 } |
390 return token; | 412 return token; |
391 } | 413 } |
OLD | NEW |