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