OLD | NEW |
| 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 |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 /** |
| 6 * AST describing all information about Dart libraries required to render |
| 7 * Dart documentation. |
| 8 */ |
1 library ast; | 9 library ast; |
2 | 10 |
3 import 'package:web_ui/safe_html.dart'; | 11 import 'package:web_ui/safe_html.dart'; |
4 import 'markdown.dart' as md; | 12 import 'markdown.dart' as md; |
5 | 13 |
6 /** | 14 /** |
7 * Top level data model for the app. | 15 * Top level data model for the app. |
8 * Mapping from String ids to [LibraryElement] objects describing all currently | 16 * Mapping from String ids to [LibraryElement] objects describing all currently |
9 * loaded libraries. All code must be written to work properly if more libraries | 17 * loaded libraries. All code must be written to work properly if more libraries |
10 * are loaded incrementally. | 18 * are loaded incrementally. |
11 */ | 19 */ |
12 Map<String, LibraryElement> libraries = <LibraryElement>{}; | 20 Map<String, LibraryElement> libraries = <LibraryElement>{}; |
13 | 21 |
14 List<String> LIBRARY_KINDS = ['variable', 'property', 'method', 'class', 'except
ion', 'typedef']; | 22 /** |
15 List<String> CLASS_KINDS = ['constructor', 'variable', 'property', 'method', 'op
erator']; | 23 * Children of a library are shown in the UI grouped by type sorted in the order |
| 24 * specified by this list. |
| 25 */ |
| 26 List<String> LIBRARY_ITEMS = ['variable', 'property', 'method', 'class', |
| 27 'exception', 'typedef']; |
| 28 /** |
| 29 * Children of a class are shown in the UI grouped by type sorted in the order |
| 30 * specified by this list. |
| 31 */ |
| 32 List<String> CLASS_ITEMS = ['constructor', 'variable', 'property', 'method', |
| 33 'operator']; |
16 // TODO(jacobr): add package kinds? | 34 // TODO(jacobr): add package kinds? |
17 | 35 |
18 // TODO(jacobr): i18n | 36 // TODO(jacobr): i18n |
19 /** | 37 /** |
20 * Pretty names for the various kinds displayed. | 38 * Pretty names for the various kinds displayed. |
21 */ | 39 */ |
22 final KIND_TITLES = {'property': 'Properties', | 40 final KIND_TITLES = { |
23 'variable': 'Variables', | 41 'property': 'Properties', |
24 'method': 'Functions', | 42 'variable': 'Variables', |
25 'constructor': 'Constructors', | 43 'method': 'Functions', |
26 'class': 'Classes', | 44 'constructor': 'Constructors', |
27 'operator': 'Operators', | 45 'class': 'Classes', |
28 'typedef': 'Typedefs', | 46 'operator': 'Operators', |
29 'exception': 'Exceptions' | 47 'typedef': 'Typedefs', |
| 48 'exception': 'Exceptions' |
30 }; | 49 }; |
31 | 50 |
32 /** | 51 /** |
33 * Block of elements to render summary documentation for that all share the | |
34 * same kind. | |
35 * | |
36 * For example, all properties, all functions, or all constructors. | |
37 */ | |
38 class ElementBlock { | |
39 String kind; | |
40 List<Element> elements; | |
41 | |
42 ElementBlock(this.kind, this.elements); | |
43 | |
44 String get kindTitle => KIND_TITLES[kind]; | |
45 } | |
46 | |
47 Reference jsonDeserializeReference(Map json) { | |
48 return json != null ? new Reference(json) : null; | |
49 } | |
50 | |
51 /** | |
52 * Deserializes JSON into [Element] or [Reference] objects. | |
53 */ | |
54 Element jsonDeserialize(Map json, Element parent) { | |
55 if (json == null) return null; | |
56 if (!json.containsKey('kind')) { | |
57 throw "Unable to deserialize $json"; | |
58 } | |
59 | |
60 switch (json['kind']) { | |
61 case 'class': | |
62 return new ClassElement(json, parent); | |
63 case 'typedef': | |
64 return new TypedefElement(json, parent); | |
65 case 'typeparam': | |
66 return new TypeParameterElement(json, parent); | |
67 case 'library': | |
68 return new LibraryElement(json, parent); | |
69 case 'method': | |
70 return new MethodElement(json, parent); | |
71 case 'property': | |
72 return new PropertyElement(json, parent); | |
73 case 'constructor': | |
74 return new ConstructorElement(json, parent); | |
75 case 'variable': | |
76 return new VariableElement(json, parent); | |
77 case 'param': | |
78 return new ParameterElement(json, parent); | |
79 default: | |
80 return new Element(json, parent); | |
81 } | |
82 } | |
83 | |
84 List<Element> jsonDeserializeArray(List json, Element parent) { | |
85 var ret = <Element>[]; | |
86 if (json != null) { | |
87 for (Map elementJson in json) { | |
88 ret.add(jsonDeserialize(elementJson, parent)); | |
89 } | |
90 } | |
91 return ret; | |
92 } | |
93 | |
94 List<Reference> jsonDeserializeReferenceArray(List json) { | |
95 var ret = <Reference>[]; | |
96 if (json != null) { | |
97 for (Map referenceJson in json) { | |
98 ret.add(new Reference(referenceJson)); | |
99 } | |
100 } | |
101 return ret; | |
102 } | |
103 | |
104 /** | |
105 * Reference to an [Element]. | 52 * Reference to an [Element]. |
106 */ | 53 */ |
107 class Reference { | 54 class Reference { |
108 final String refId; | 55 final String refId; |
109 final String name; | 56 final String name; |
| 57 final List<Reference> arguments; |
| 58 |
110 Reference(Map json) : | 59 Reference(Map json) : |
111 name = json['name'], | 60 name = json['name'], |
112 refId = json['refId']; | 61 refId = json['refId'], |
| 62 arguments = _jsonDeserializeReferenceArray(json['arguments']); |
| 63 |
| 64 /** |
| 65 * Short description appropriate for displaying in a tree control or other |
| 66 * situtation where a short description is required. |
| 67 */ |
| 68 String get shortDescription { |
| 69 if (arguments.isEmpty) { |
| 70 return name; |
| 71 } else { |
| 72 var params = Strings.join( |
| 73 arguments.map((param) => param.shortDescription), ', '); |
| 74 return '$name<$params>'; |
| 75 } |
| 76 } |
113 } | 77 } |
114 | 78 |
115 /** | 79 /** |
116 * Lookup a library based on the [libraryId]. | 80 * Lookup a library based on the [libraryId]. |
117 * | |
118 * If the library cannot be found, a stub dummy [Library] will be returned. | |
119 */ | 81 */ |
120 LibraryElement lookupLibrary(String libraryId) { | 82 LibraryElement lookupLibrary(String libraryId) { |
121 var library = libraries[libraryId]; | 83 return libraries[libraryId]; |
122 if (library == null) { | |
123 library = new LibraryElement.stub(libraryId, null); | |
124 } | |
125 return library; | |
126 } | 84 } |
127 | 85 |
128 /** | 86 /** |
129 * Resolve the [Element] matching the [referenceId]. | 87 * Resolve the [Element] matching the [referenceId]. |
130 * | 88 * |
131 * If the Element cannot be found, a stub dummy [Element] will be returned. | 89 * If the Element cannot be found, a stub dummy [Element] will be returned. |
132 */ | 90 */ |
133 Element lookupReferenceId(String referenceId) { | 91 Element lookupReferenceId(String referenceId) { |
134 var parts = referenceId.split(new RegExp('/')); | 92 var parts = referenceId.split(new RegExp('/')); |
135 Element current = lookupLibrary(parts.first); | 93 Element current = lookupLibrary(parts.first); |
136 var result = <Element>[current]; | 94 for (var i = 1; i < parts.length && current != null; i++) { |
137 for (var i = 1; i < parts.length; i++) { | |
138 var id = parts[i]; | 95 var id = parts[i]; |
139 var next = null; | 96 var next = null; |
140 for (var child in current.children) { | 97 for (var child in current.children) { |
141 if (child.id == id) { | 98 if (child.id == id) { |
142 next = child; | 99 next = child; |
143 break; | 100 break; |
144 } | 101 } |
145 } | 102 } |
146 if (next == null) { | |
147 next = new Element.stub(id, current); | |
148 } | |
149 current = next; | 103 current = next; |
150 } | 104 } |
151 return current; | 105 return current; |
152 } | 106 } |
153 | 107 |
154 /** | 108 /** |
155 * Invoke [callback] on every [Element] in the ast. | 109 * Invoke [callback] on every [Element] in the ast. |
156 */ | 110 */ |
157 _traverseWorld(void callback(Element)) { | 111 _traverseWorld(void callback(Element)) { |
158 for (var library in libraries.values) { | 112 for (var library in libraries.values) { |
159 library.traverse(callback); | 113 library.traverse(callback); |
160 } | 114 } |
161 } | 115 } |
162 | 116 |
163 // TODO(jacobr): remove this method when templates handle safe HTML containing | 117 // TODO(jacobr): remove this method when templates handle [SafeHTML] containing |
| 118 // multiple top level nodes correct. |
164 SafeHtml _markdownToSafeHtml(String text) { | 119 SafeHtml _markdownToSafeHtml(String text) { |
165 // We currently have to insert an extra span for now because of | 120 // We currently have to insert an extra span for now because of |
166 // https://github.com/dart-lang/dart-web-components/issues/212 | 121 // https://github.com/dart-lang/web-ui/issues/212 |
167 return new SafeHtml.unsafe(text != null && !text.isEmpty ? | 122 return new SafeHtml.unsafe(text != null && !text.isEmpty ? |
168 '<span>${md.markdownToHtml(text)}</span>' : '<span><span>'); | 123 '<span>${md.markdownToHtml(text)}</span>' : '<span><span>'); |
169 } | 124 } |
170 | 125 |
171 /** | 126 /** |
172 * Specifies the order elements should appear in the UI. | |
173 */ | |
174 int elementUiOrder(Element a, Element b) { | |
175 if (a.isPrivate != b.isPrivate) { | |
176 return a.isPrivate == true ? 1 : -1; | |
177 } | |
178 return a.name.compareTo(b.name); | |
179 } | |
180 | |
181 /** | |
182 * Base class for all elements in the AST. | 127 * Base class for all elements in the AST. |
183 */ | 128 */ |
184 class Element { | 129 class Element implements Comparable { |
185 final Element parent; | 130 final Element parent; |
| 131 |
186 /** Human readable type name for the node. */ | 132 /** Human readable type name for the node. */ |
187 final String rawKind; | 133 final String rawKind; |
| 134 |
188 /** Human readable name for the element. */ | 135 /** Human readable name for the element. */ |
189 final String name; | 136 final String name; |
| 137 |
190 /** Id for the node that is unique within its parent's children. */ | 138 /** Id for the node that is unique within its parent's children. */ |
191 final String id; | 139 final String id; |
| 140 |
192 /** Raw text of the comment associated with the Element if any. */ | 141 /** Raw text of the comment associated with the Element if any. */ |
193 final String comment; | 142 final String comment; |
| 143 |
194 /** Whether the node is private. */ | 144 /** Whether the node is private. */ |
195 final bool isPrivate; | 145 final bool isPrivate; |
196 | 146 |
197 final String _uri; | |
198 final String _line; | |
199 | |
200 /** Children of the node. */ | 147 /** Children of the node. */ |
201 List<Element> children; | 148 List<Element> children; |
202 | 149 |
203 /** Whether the [Element] is currently being loaded. */ | 150 /** Whether the [Element] is currently being loaded. */ |
204 final bool loading; | 151 final bool loading; |
205 | 152 |
| 153 final String _uri; |
| 154 final String _line; |
206 String _refId; | 155 String _refId; |
207 | |
208 Map _members; | |
209 SafeHtml _commentHtml; | 156 SafeHtml _commentHtml; |
210 List<Element> _references; | 157 List<Element> _references; |
| 158 List<Element> _typeParameters; |
211 | 159 |
212 Element(Map json, this.parent) : | 160 Element(Map json, this.parent) : |
213 name = json['name'], | 161 name = json['name'], |
214 rawKind = json['kind'], | 162 rawKind = json['kind'], |
215 id = json['id'], | 163 id = json['id'], |
216 comment = json['comment'], | 164 comment = json['comment'], |
217 isPrivate = json['isPrivate'], | 165 isPrivate = json['isPrivate'] == true, |
218 _uri = json['uri'], | 166 _uri = json['uri'], |
219 _line = json['line'], | 167 _line = json['line'], |
220 loading = false { | 168 loading = false { |
221 children = jsonDeserializeArray(json['children'], this); | 169 children = _jsonDeserializeArray(json['children'], this); |
222 } | 170 } |
223 | 171 |
224 /** | 172 /** |
225 * Returns a kind name that make sense for the UI rather than the AST | 173 * Returns a kind name that make sense for the UI rather than the AST |
226 * kinds. For example, setters are considered properties instead of | 174 * kinds. For example, setters are considered properties instead of |
227 * methods. | 175 * methods in the UI but not the AST. |
228 */ | 176 */ |
229 String get uiKind => kind; | 177 String get uiKind => kind; |
230 | 178 |
| 179 /** |
| 180 * Longer possibly multiple word description of the [kind]. |
| 181 */ |
| 182 String get kindDescription => uiKind; |
| 183 |
231 /** Invoke [callback] on this [Element] and all descendants. */ | 184 /** Invoke [callback] on this [Element] and all descendants. */ |
232 void traverse(void callback(Element)) { | 185 void traverse(void callback(Element)) { |
233 callback(this); | 186 callback(this); |
234 for (var child in children) { | 187 for (var child in children) { |
235 callback(child); | 188 callback(child); |
236 } | 189 } |
237 } | 190 } |
238 | 191 |
239 /** | 192 /** |
240 * Uri containing the definition of the element. | 193 * Uri containing the source code for the definition of the element. |
241 */ | 194 */ |
242 String get uri { | 195 String get uri => _uri != null ? _uri : (parent != null ? parent.uri : null); |
243 Element current = this; | |
244 while (current != null) { | |
245 if (current._uri != null) return current._uri; | |
246 current = current.parent; | |
247 } | |
248 return null; | |
249 } | |
250 | 196 |
251 /** | 197 /** |
252 * Line in the original source file that starts the definition of the element. | 198 * Line in the original source file that begins the definition of the element. |
253 */ | 199 */ |
254 String get line { | 200 String get line => _line != null ? |
255 Element current = this; | 201 _line : (parent != null ? parent.line : null); |
256 while (current != null) { | |
257 if (current._line != null) return current._line; | |
258 current = current.parent; | |
259 } | |
260 return null; | |
261 } | |
262 | |
263 Element.stub(this.id, this.parent) : | |
264 name = '???', // TODO(jacobr): remove/add | |
265 _uri = null, | |
266 _line = null, | |
267 comment = null, | |
268 rawKind = null, | |
269 children = <Element>[], | |
270 isPrivate = null, | |
271 loading = true; | |
272 | 202 |
273 /** | 203 /** |
274 * Globally unique identifier for this element. | 204 * Globally unique identifier for this element. |
275 */ | 205 */ |
276 String get refId { | 206 String get refId { |
277 if (_refId == null) { | 207 if (_refId == null) { |
278 if (parent == null) { | 208 _refId = (parent == null) ? id : '${parent.refId}/$id'; |
279 _refId = id; | |
280 } else { | |
281 _refId = '${parent.refId}/$id'; | |
282 } | |
283 } | 209 } |
284 return _refId; | 210 return _refId; |
285 } | 211 } |
286 | 212 |
287 /** | 213 /** |
288 * Whether this [Element] references the specified [referenceId]. | 214 * Whether this [Element] references the specified [referenceId]. |
289 */ | 215 */ |
290 bool hasReference(String referenceId) { | 216 bool hasReference(String referenceId) => |
291 for (var child in children) { | 217 children.some((child) => child.hasReference(referenceId)); |
292 if (child.hasReference(referenceId)) { | |
293 return true; | |
294 } | |
295 } | |
296 return false; | |
297 } | |
298 | 218 |
299 /** Returns all [Element]s that reference this [Element]. */ | 219 /** Returns all [Element]s that reference this [Element]. */ |
300 List<Element> get references { | 220 List<Element> get references { |
301 if (_references == null) { | 221 if (_references == null) { |
302 _references = <Element>[]; | 222 _references = <Element>[]; |
303 // TODO(jacobr): change to filterWorld and tweak meaning. | |
304 _traverseWorld((element) { | 223 _traverseWorld((element) { |
305 if (element.hasReference(refId)) { | 224 if (element.hasReference(refId)) { |
306 _references.add(element); | 225 _references.add(element); |
307 } | 226 } |
308 }); | 227 }); |
309 } | 228 } |
310 return _references; | 229 return _references; |
311 } | 230 } |
312 | 231 |
313 // TODO(jacobr): write without recursion. | 232 /** Path from the root of the tree to this [Element]. */ |
314 /** | |
315 * Path from this [Element] to the root of the tree starting at the root. | |
316 */ | |
317 List<Element> get path { | 233 List<Element> get path { |
318 if (parent == null) { | 234 if (parent == null) { |
319 return <Element>[this]; | 235 return <Element>[this]; |
320 } else { | 236 } else { |
321 return parent.path..add(this); | 237 return parent.path..add(this); |
322 } | 238 } |
| 239 // TODO(jacobr): replace this code with: |
| 240 // return parent == null ? <Element>[this]) : parent.path..add(this); |
| 241 // once http://code.google.com/p/dart/issues/detail?id=7665 is fixed. |
| 242 } |
| 243 |
| 244 List<Element> get typeParameters { |
| 245 if (_typeParameters == null) { |
| 246 _typeParameters = _filterByKind('typeparam'); |
| 247 } |
| 248 return _typeParameters; |
323 } | 249 } |
324 | 250 |
325 /** | 251 /** |
326 * [SafeHtml] for the comment associated with this [Element] generated from | 252 * [SafeHtml] for the comment associated with this [Element] generated from |
327 * the markdow comment associated with the element. | 253 * the markdow comment associated with the element. |
328 */ | 254 */ |
329 SafeHtml get commentHtml { | 255 SafeHtml get commentHtml { |
330 if (_commentHtml == null) { | 256 if (_commentHtml == null) { |
331 _commentHtml = _markdownToSafeHtml(comment); | 257 _commentHtml = _markdownToSafeHtml(comment); |
332 } | 258 } |
333 return _commentHtml; | 259 return _commentHtml; |
334 } | 260 } |
335 | 261 |
336 /** | 262 /** |
337 * Short description appropriate for displaying in a tree control or other | 263 * Short description appropriate for displaying in a tree control or other |
338 * situtation where a short description is required. | 264 * situtation where a short description is required. |
339 */ | 265 */ |
340 String get shortDescription => name; | 266 String get shortDescription { |
| 267 if (typeParameters.isEmpty) { |
| 268 return name; |
| 269 } else { |
| 270 var params = Strings.join( |
| 271 typeParameters.map((param) => param.shortDescription), |
| 272 ', '); |
| 273 return '$name<$params>'; |
| 274 } |
| 275 } |
341 | 276 |
342 /** Possibly normalized representation of the node kind. */ | 277 /** |
| 278 * Ui specific representation of the node kind. |
| 279 * For example, currently operators are considered their own kind even though |
| 280 * they aren't their own kind in the AST. |
| 281 */ |
343 String get kind => rawKind; | 282 String get kind => rawKind; |
344 | 283 |
345 /** | 284 /** |
346 * Generate blocks of Elements for each kind in the list of [desiredKinds]. | 285 * Generate blocks of Elements for each kind in the list of [desiredKinds]. |
347 * | 286 * |
348 * This is helpful when rendering UI that segments members into blocks. | 287 * This is helpful when rendering UI that segments members into blocks. |
349 * Uses the kind types that make sense for the UI rather than the AST | 288 * Uses the kind types that make sense for the UI rather than the AST |
350 * kinds. For example, setters are considered properties instead of methods. | 289 * kinds. For example, setters are considered properties instead of methods. |
351 */ | 290 */ |
352 List<ElementBlock> _createElementBlocks(List<String> desiredKinds) { | 291 List<ElementBlock> _createElementBlocks(List<String> desiredKinds) { |
353 var blockMap = new Map<String, List<Element>>(); | 292 var blockMap = new Map<String, List<Element>>(); |
354 for (var child in children) { | 293 for (var child in children) { |
355 if (desiredKinds.contains(child.uiKind)) { | 294 if (desiredKinds.contains(child.uiKind)) { |
356 blockMap.putIfAbsent(child.uiKind, () => <Element>[]).add(child); | 295 blockMap.putIfAbsent(child.uiKind, () => <Element>[]).add(child); |
357 } | 296 } |
358 } | 297 } |
359 | 298 |
360 var blocks = <ElementBlock>[]; | 299 var blocks = <ElementBlock>[]; |
361 for (var kind in desiredKinds) { | 300 for (var kind in desiredKinds) { |
362 var elements = blockMap[kind]; | 301 var elements = blockMap[kind]; |
363 if (elements != null) { | 302 if (elements != null) { |
364 blocks.add(new ElementBlock(kind, elements..sort(elementUiOrder))); | 303 blocks.add(new ElementBlock(kind, elements..sort())); |
365 } | 304 } |
366 } | 305 } |
367 return blocks; | 306 return blocks; |
368 } | 307 } |
369 | 308 |
370 List<Element> _filterByKind(String kind) => | 309 List<Element> _filterByKind(String kind) => |
371 children.filter((child) => child.kind == kind); | 310 children.filter((child) => child.kind == kind); |
372 | 311 |
373 Map<String, Element> _mapForKind(String kind) { | 312 Map<String, Element> _mapForKind(String kind) { |
374 Map ret = {}; | 313 Map ret = {}; |
375 if (children != null) { | 314 if (children == null) return ret; |
376 for (var child in children) { | 315 |
377 if (child.kind == kind) { | 316 for (var child in children) { |
378 ret[child.id] = child; | 317 if (child.kind == kind) { |
379 } | 318 ret[child.id] = child; |
380 } | 319 } |
381 } | 320 } |
382 return ret; | 321 return ret; |
383 } | 322 } |
384 | 323 |
385 Map<String, Element> _mapForKinds(Map<String, Element> kinds) { | 324 /** |
386 Map ret = {}; | 325 * Specifies the order elements should appear in the UI. |
387 if (children != null) { | 326 */ |
388 for (var child in children) { | 327 int compareTo(Element other) { |
389 if (kinds.containsKey(child.kind)) { | 328 if (isPrivate != other.isPrivate) { |
390 ret[child.id] = child; | 329 return other.isPrivate ? 1 : -1; |
391 } | |
392 } | |
393 } | 330 } |
394 return ret; | 331 return name.compareTo(other.name); |
395 } | |
396 | |
397 Map<String, Element> get members { | |
398 if (_members == null) { | |
399 // TODO(jacobr): include properties???!? | |
400 _members = _mapForKinds({'method': true, 'property' : true}); | |
401 } | |
402 return _members; | |
403 } | 332 } |
404 } | 333 } |
405 | 334 |
406 /** | 335 /** |
407 * [Element] describing a Dart library. | 336 * [Element] describing a Dart library. |
408 * | 337 * |
409 * Adds convenience helpers for quickly accessing data about libraries. | 338 * Adds convenience helpers for quickly accessing data about libraries. |
410 */ | 339 */ |
411 class LibraryElement extends Element { | 340 class LibraryElement extends Element { |
412 Map<String, ClassElement> _classes; | 341 Map<String, ClassElement> _classes; |
413 List<ClassElement> _sortedClasses; | 342 List<ClassElement> _sortedClasses; |
414 List<ElementBlock> _childBlocks; | 343 List<ElementBlock> _childBlocks; |
415 | 344 |
416 LibraryElement(json, Element parent) : super(json, parent); | 345 LibraryElement(json, Element parent) : super(json, parent); |
417 LibraryElement.stub(String id, Element parent) : super.stub(id, parent); | |
418 | 346 |
419 /** Returns all classes defined by the library. */ | 347 /** Returns all classes defined by the library. */ |
420 Map<String, ClassElement> get classes { | 348 Map<String, ClassElement> get classes { |
421 if (_classes == null) { | 349 if (_classes == null) { |
422 _classes = _mapForKind('class'); | 350 _classes = _mapForKind('class'); |
423 } | 351 } |
424 return _classes; | 352 return _classes; |
425 } | 353 } |
426 | 354 |
427 /** | 355 /** |
428 * Returns all classes defined by the library sorted name and whether they | 356 * Returns all classes defined by the library sorted name and whether they |
429 * are private. | 357 * are private. |
430 */ | 358 */ |
431 List<ClassElement> get sortedClasses { | 359 List<ClassElement> get sortedClasses { |
432 if (_sortedClasses == null) { | 360 if (_sortedClasses == null) { |
433 _sortedClasses = []..addAll(classes.values)..sort(elementUiOrder); | 361 _sortedClasses = []..addAll(classes.values)..sort(); |
434 } | 362 } |
435 return _sortedClasses; | 363 return _sortedClasses; |
436 } | 364 } |
437 | 365 |
438 /** | 366 /** |
439 * Returns all blocks of elements that should be rendered by UI summarizing | 367 * Returns all blocks of elements that should be rendered by UI summarizing |
440 * the Library. | 368 * the Library. |
441 */ | 369 */ |
442 List<ElementBlock> get childBlocks { | 370 List<ElementBlock> get childBlocks { |
443 if (_childBlocks == null) _childBlocks = _createElementBlocks(LIBRARY_KINDS)
; | 371 if (_childBlocks == null) { |
| 372 _childBlocks = _createElementBlocks(LIBRARY_ITEMS); |
| 373 } |
444 return _childBlocks; | 374 return _childBlocks; |
445 } | 375 } |
446 } | 376 } |
447 | 377 |
448 /** | 378 /** |
449 * [Element] describing a Dart class. | 379 * [Element] describing a Dart class. |
450 */ | 380 */ |
451 class ClassElement extends Element { | 381 class ClassElement extends Element { |
| 382 |
452 /** Members of the class grouped into logical blocks. */ | 383 /** Members of the class grouped into logical blocks. */ |
453 List<ElementBlock> _childBlocks; | 384 List<ElementBlock> _childBlocks; |
| 385 |
454 /** Interfaces the class implements. */ | 386 /** Interfaces the class implements. */ |
455 final List<Reference> interfaces; | 387 final List<Reference> interfaces; |
| 388 |
456 /** Superclass of this class. */ | 389 /** Superclass of this class. */ |
457 final Reference superclass; | 390 final Reference superclass; |
458 | 391 |
| 392 /** Whether the class is abstract. */ |
| 393 final bool isAbstract; |
| 394 |
459 List<ClassElement> _superclasses; | 395 List<ClassElement> _superclasses; |
460 List<ClassElement> _subclasses; | 396 List<ClassElement> _subclasses; |
461 | 397 |
462 ClassElement(Map json, Element parent) | 398 ClassElement(Map json, Element parent) |
463 : super(json, parent), | 399 : super(json, parent), |
464 interfaces = jsonDeserializeReferenceArray(json['interfaces']), | 400 interfaces = _jsonDeserializeReferenceArray(json['interfaces']), |
465 superclass = jsonDeserializeReference(json['superclass']); | 401 superclass = jsonDeserializeReference(json['superclass']), |
466 | 402 isAbstract = json['isAbstract'] == true; |
467 ClassElement.stub(String id, Element parent) | |
468 : super.stub(id, parent), | |
469 interfaces = [], | |
470 superclass = null; | |
471 | 403 |
472 /** Returns all superclasses of this class. */ | 404 /** Returns all superclasses of this class. */ |
473 List<ClassElement> get superclasses { | 405 List<ClassElement> get superclasses { |
474 if (_superclasses == null) { | 406 if (_superclasses == null) { |
475 _superclasses = <ClassElement>[]; | 407 _superclasses = <ClassElement>[]; |
476 addSuperclasses(clazz) { | 408 addSuperclasses(clazz) { |
477 if (clazz.superclass != null) { | 409 if (clazz.superclass != null) { |
478 ClassElement superclassElement = | 410 var superclassElement = lookupReferenceId(clazz.superclass.refId); |
479 lookupReferenceId(clazz.superclass.refId); | |
480 addSuperclasses(superclassElement); | 411 addSuperclasses(superclassElement); |
481 _superclasses.add(superclassElement); | 412 _superclasses.add(superclassElement); |
482 } | 413 } |
483 } | 414 } |
484 addSuperclasses(this); | 415 addSuperclasses(this); |
485 } | 416 } |
486 return _superclasses; | 417 return _superclasses; |
487 } | 418 } |
488 | 419 |
| 420 String get kindDescription => |
| 421 isAbstract ? 'abstract $uiKind' : uiKind; |
| 422 |
489 /** | 423 /** |
490 * Returns classes that directly extend or implement this class. | 424 * Returns classes that directly extend or implement this class. |
491 */ | 425 */ |
492 List<ClassElement> get subclasses { | 426 List<ClassElement> get subclasses { |
493 if (_subclasses == null) { | 427 if (_subclasses == null) { |
494 _subclasses = <ClassElement>[]; | 428 _subclasses = <ClassElement>[]; |
495 for (var library in libraries.values) { | 429 for (var library in libraries.values) { |
496 for (ClassElement candidateClass in library.sortedClasses) { | 430 for (ClassElement candidateClass in library.sortedClasses) { |
497 if (candidateClass.implementsOrExtends(refId)) { | 431 if (candidateClass.implementsOrExtends(refId)) { |
498 _subclasses.add(candidateClass); | 432 _subclasses.add(candidateClass); |
(...skipping 13 matching lines...) Expand all Loading... |
512 if (interface.refId == referenceId) return true; | 446 if (interface.refId == referenceId) return true; |
513 } | 447 } |
514 return superclass != null && superclass.refId == referenceId; | 448 return superclass != null && superclass.refId == referenceId; |
515 } | 449 } |
516 | 450 |
517 /** | 451 /** |
518 * Returns blocks of elements clustered by kind ordered in the desired | 452 * Returns blocks of elements clustered by kind ordered in the desired |
519 * order for describing a class definition. | 453 * order for describing a class definition. |
520 */ | 454 */ |
521 List<ElementBlock> get childBlocks { | 455 List<ElementBlock> get childBlocks { |
522 if (_childBlocks == null) _childBlocks = _createElementBlocks(CLASS_KINDS); | 456 if (_childBlocks == null) _childBlocks = _createElementBlocks(CLASS_ITEMS); |
523 return _childBlocks; | 457 return _childBlocks; |
524 } | 458 } |
525 } | 459 } |
526 | 460 |
| 461 /** |
| 462 * Element describing a typedef. |
| 463 */ |
527 class TypedefElement extends Element { | 464 class TypedefElement extends Element { |
528 final Reference returnType; | 465 final Reference returnType; |
529 List<ParameterElement> _parameters; | 466 List<Element> _parameters; |
530 | 467 |
531 TypedefElement(Map json, Element parent) : super(json, parent), | 468 TypedefElement(Map json, Element parent) : super(json, parent), |
532 returnType = jsonDeserializeReference(json['returnType']); | 469 returnType = jsonDeserializeReference(json['returnType']); |
533 | 470 |
534 /** | 471 /** |
535 * Returns a list of the parameters of the typedef. | 472 * Returns a list of the parameters of the typedef. |
536 */ | 473 */ |
537 List<ParameterElement> get parameters { | 474 List<Element> get parameters { |
538 if (_parameters == null) { | 475 if (_parameters == null) { |
539 _parameters = _filterByKind('param'); | 476 _parameters = _filterByKind('param'); |
540 } | 477 } |
541 return _parameters; | 478 return _parameters; |
542 } | 479 } |
543 } | 480 } |
544 | 481 |
545 /** | 482 /** |
546 * [Element] describing a method which may be a regular method, a setter, or an | 483 * [Element] describing a method which may be a regular method, a setter, or an |
547 * operator. | 484 * operator. |
548 */ | 485 */ |
549 abstract class MethodElementBase extends Element { | 486 abstract class MethodLikeElement extends Element { |
550 | 487 |
551 final bool isOperator; | 488 final bool isOperator; |
552 final bool isStatic; | 489 final bool isStatic; |
553 final bool isSetter; | 490 final bool isSetter; |
554 | 491 |
555 MethodElementBase(Map json, Element parent) | 492 MethodLikeElement(Map json, Element parent) |
556 : super(json, parent), | 493 : super(json, parent), |
557 isOperator = json['isOperator'] == true, | 494 isOperator = json['isOperator'] == true, |
558 isStatic = json['isStatic'] == true, | 495 isStatic = json['isStatic'] == true, |
559 isSetter = json['isSetter'] == true; | 496 isSetter = json['isSetter'] == true; |
560 | 497 |
561 bool hasReference(String referenceId) { | 498 bool hasReference(String referenceId) { |
562 if (super.hasReference(referenceId)) return true; | 499 if (super.hasReference(referenceId)) return true; |
563 return returnType != null && returnType.refId == referenceId; | 500 return returnType != null && returnType.refId == referenceId; |
564 } | 501 } |
565 | 502 |
566 String get uiKind => isSetter ? 'property' : kind; | 503 String get uiKind => isSetter ? 'property' : kind; |
567 | 504 |
568 /** | 505 /** |
569 * Returns a plain text short description of the method suitable for rendering | 506 * Returns a plain text short description of the method suitable for rendering |
570 * in a tree control or other case where a short method description is | 507 * in a tree control or other case where a short method description is |
571 * required. | 508 * required. |
572 */ | 509 */ |
573 String get shortDescription { | 510 String get shortDescription { |
574 if (isSetter == true) { | 511 if (isSetter) { |
575 var sb = new StringBuffer('${name.substring(0, name.length-1)}'); | 512 var sb = new StringBuffer('${name.substring(0, name.length - 1)}'); |
576 if (!parameters.isEmpty && parameters.first != null | 513 if (!parameters.isEmpty && parameters.first != null |
577 && parameters.first.type != null) { | 514 && parameters.first.type != null) { |
578 sb..add(' ')..add(parameters.first.type.name); | 515 sb..add(' ')..add(parameters.first.type.name); |
579 } | 516 } |
580 return sb.toString(); | 517 return sb.toString(); |
581 } | 518 } |
582 return '$name(${Strings.join(parameters.map( | 519 return '$name(${Strings.join(parameters.map( |
583 (arg) => arg.type != null ? arg.type.name : ''), ', ')})'; | 520 (arg) => arg.type != null ? arg.type.name : ''), ', ')})'; |
584 } | 521 } |
585 | 522 |
586 Reference get returnType; | 523 Reference get returnType; |
587 List<ParameterElement> _parameters; | 524 List<Element> _parameters; |
588 | 525 |
589 /** | 526 /** |
590 * Returns a list of the parameters of the Method. | 527 * Returns a list of the parameters of the Method. |
591 */ | 528 */ |
592 List<ParameterElement> get parameters { | 529 List<Element> get parameters { |
593 if (_parameters == null) { | 530 if (_parameters == null) { |
594 _parameters = _filterByKind('param'); | 531 _parameters = _filterByKind('param'); |
595 } | 532 } |
596 return _parameters; | 533 return _parameters; |
597 } | 534 } |
598 | 535 |
599 // For UI purposes we want to treat operators as their own kind. | 536 /// For UI purposes we want to treat operators as their own kind. |
600 String get kind => isOperator ? 'operator' : rawKind; | 537 String get kind => isOperator ? 'operator' : rawKind; |
601 } | 538 } |
602 | 539 |
603 /** | 540 /** |
604 * Element describing a parameter. | 541 * Element describing a parameter. |
605 */ | 542 */ |
606 class ParameterElement extends Element { | 543 class ParameterElement extends Element { |
607 /** Type of the parameter. */ | 544 /** Type of the parameter. */ |
608 final Reference type; | 545 final Reference type; |
609 /** Whether the parameter is optional. */ | 546 /** Whether the parameter is optional. */ |
(...skipping 14 matching lines...) Expand all Loading... |
624 * Element describing a generic type parameter. | 561 * Element describing a generic type parameter. |
625 */ | 562 */ |
626 class TypeParameterElement extends Element { | 563 class TypeParameterElement extends Element { |
627 /** Upper bound for the parameter. */ | 564 /** Upper bound for the parameter. */ |
628 Reference upperBound; | 565 Reference upperBound; |
629 | 566 |
630 TypeParameterElement(Map json, Element parent) : | 567 TypeParameterElement(Map json, Element parent) : |
631 super(json, parent), | 568 super(json, parent), |
632 upperBound = jsonDeserializeReference(json['upperBound']); | 569 upperBound = jsonDeserializeReference(json['upperBound']); |
633 | 570 |
| 571 String get shortDescription { |
| 572 if (upperBound == null) { |
| 573 return name; |
| 574 } else { |
| 575 return '$name extends ${upperBound.shortDescription}'; |
| 576 } |
| 577 } |
| 578 |
634 bool hasReference(String referenceId) { | 579 bool hasReference(String referenceId) { |
635 if (super.hasReference(referenceId)) return true; | 580 if (super.hasReference(referenceId)) return true; |
636 return upperBound != null && upperBound.refId == referenceId; | 581 return upperBound != null && upperBound.refId == referenceId; |
637 } | 582 } |
638 } | 583 } |
639 | 584 |
640 class MethodElement extends MethodElementBase { | 585 /** |
| 586 * Element describing a method. |
| 587 */ |
| 588 class MethodElement extends MethodLikeElement { |
641 | 589 |
642 final Reference returnType; | 590 final Reference returnType; |
643 | 591 |
644 MethodElement(Map json, Element parent) : super(json, parent), | 592 MethodElement(Map json, Element parent) : super(json, parent), |
645 returnType = jsonDeserializeReference(json['returnType']); | 593 returnType = jsonDeserializeReference(json['returnType']); |
646 } | 594 } |
647 | 595 |
648 class PropertyElement extends MethodElementBase { | 596 /** |
| 597 * Element describing a property getter. |
| 598 */ |
| 599 class PropertyElement extends MethodLikeElement { |
649 final Reference returnType; | 600 final Reference returnType; |
650 | 601 |
651 String get shortDescription => name; | 602 String get shortDescription => name; |
652 | 603 |
653 PropertyElement(Map json, Element parent) : super(json, parent), | 604 PropertyElement(Map json, Element parent) : super(json, parent), |
654 returnType = jsonDeserializeReference(json['ref']); | 605 returnType = jsonDeserializeReference(json['ref']); |
655 } | 606 } |
656 | 607 |
657 class VariableElement extends MethodElementBase { | 608 /** |
| 609 * Element describing a variable. |
| 610 */ |
| 611 class VariableElement extends MethodLikeElement { |
658 final Reference returnType; | 612 final Reference returnType; |
659 /** Whether this variable is final. */ | 613 /** Whether this variable is final. */ |
660 final bool isFinal; | 614 final bool isFinal; |
661 | 615 |
662 String get shortDescription => name; | 616 String get shortDescription => name; |
663 | 617 |
664 VariableElement(Map json, Element parent) : super(json, parent), | 618 VariableElement(Map json, Element parent) : super(json, parent), |
665 returnType = jsonDeserializeReference(json['ref']), | 619 returnType = jsonDeserializeReference(json['ref']), |
666 isFinal = json['isFinal']; | 620 isFinal = json['isFinal']; |
667 } | 621 } |
668 | 622 |
669 class ConstructorElement extends MethodElementBase { | 623 /** |
| 624 * Element describing a constructor. |
| 625 */ |
| 626 class ConstructorElement extends MethodLikeElement { |
670 ConstructorElement(json, Element parent) : super(json, parent); | 627 ConstructorElement(json, Element parent) : super(json, parent); |
671 | 628 |
672 Reference get returnType => null; | 629 Reference get returnType => null; |
673 } | 630 } |
| 631 |
| 632 /** |
| 633 * Block of elements to render summary documentation for all elements that share |
| 634 * the same kind. |
| 635 * |
| 636 * For example, all properties, all functions, or all constructors. |
| 637 */ |
| 638 class ElementBlock { |
| 639 String kind; |
| 640 List<Element> elements; |
| 641 |
| 642 ElementBlock(this.kind, this.elements); |
| 643 |
| 644 String get kindTitle => KIND_TITLES[kind]; |
| 645 } |
| 646 |
| 647 Reference jsonDeserializeReference(Map json) { |
| 648 return json != null ? new Reference(json) : null; |
| 649 } |
| 650 |
| 651 /** |
| 652 * Deserializes JSON into an [Element] object. |
| 653 */ |
| 654 Element jsonDeserialize(Map json, Element parent) { |
| 655 if (json == null) return null; |
| 656 |
| 657 var kind = json['kind']; |
| 658 if (kind == null) { |
| 659 throw "Unable to deserialize $json"; |
| 660 } |
| 661 |
| 662 switch (kind) { |
| 663 case 'class': |
| 664 return new ClassElement(json, parent); |
| 665 case 'typedef': |
| 666 return new TypedefElement(json, parent); |
| 667 case 'typeparam': |
| 668 return new TypeParameterElement(json, parent); |
| 669 case 'library': |
| 670 return new LibraryElement(json, parent); |
| 671 case 'method': |
| 672 return new MethodElement(json, parent); |
| 673 case 'property': |
| 674 return new PropertyElement(json, parent); |
| 675 case 'constructor': |
| 676 return new ConstructorElement(json, parent); |
| 677 case 'variable': |
| 678 return new VariableElement(json, parent); |
| 679 case 'param': |
| 680 return new ParameterElement(json, parent); |
| 681 default: |
| 682 return new Element(json, parent); |
| 683 } |
| 684 } |
| 685 |
| 686 List<Element> _jsonDeserializeArray(List json, Element parent) { |
| 687 var ret = <Element>[]; |
| 688 if (json == null) return ret; |
| 689 |
| 690 for (Map elementJson in json) { |
| 691 ret.add(jsonDeserialize(elementJson, parent)); |
| 692 } |
| 693 return ret; |
| 694 } |
| 695 |
| 696 List<Reference> _jsonDeserializeReferenceArray(List json) { |
| 697 var ret = <Reference>[]; |
| 698 if (json == null) return ret; |
| 699 |
| 700 for (Map referenceJson in json) { |
| 701 ret.add(new Reference(referenceJson)); |
| 702 } |
| 703 return ret; |
| 704 } |
OLD | NEW |