| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2014, 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 library docgen.models.indexable; | |
| 6 | |
| 7 import 'dart:collection'; | |
| 8 | |
| 9 import 'package:markdown/markdown.dart' as markdown; | |
| 10 | |
| 11 import '../exports/mirrors_util.dart' as dart2js_util; | |
| 12 import '../exports/source_mirrors.dart'; | |
| 13 | |
| 14 import '../library_helpers.dart'; | |
| 15 import 'library.dart'; | |
| 16 import 'mirror_based.dart'; | |
| 17 import 'model_helpers.dart'; | |
| 18 | |
| 19 /// An item that is categorized in our mirrorToDocgen map, as a distinct, | |
| 20 /// searchable element. | |
| 21 /// | |
| 22 /// These are items that refer to concrete entities (a Class, for example, | |
| 23 /// but not a Type, which is a "pointer" to a class) that we wish to be | |
| 24 /// globally resolvable. This includes things such as class methods and | |
| 25 /// variables, but parameters for methods are not "Indexable" as we do not want | |
| 26 /// the user to be able to search for a method based on its parameter names! | |
| 27 /// The set of indexable items also includes Typedefs, since the user can refer | |
| 28 /// to them as concrete entities in a particular scope. | |
| 29 abstract class Indexable<TMirror extends DeclarationMirror> | |
| 30 extends MirrorBased<TMirror> { | |
| 31 | |
| 32 Library get owningLibrary => owner.owningLibrary; | |
| 33 | |
| 34 /// The reference to this element based on where it is printed as a | |
| 35 /// documentation file and also the unique URL to refer to this item. | |
| 36 /// | |
| 37 /// The qualified name (for URL purposes) and the file name are the same, | |
| 38 /// of the form packageName/ClassName or packageName/ClassName.methodName. | |
| 39 /// This defines both the URL and the directory structure. | |
| 40 String get qualifiedName => packagePrefix + ownerPrefix + name; | |
| 41 | |
| 42 /// The name of the file we write this object's data into. The same as the | |
| 43 /// qualified name but with leading colons (i.e. dart:) | |
| 44 /// replaced by hyphens because of Windows. | |
| 45 String get fileName => qualifiedName.replaceFirst(":", "-"); | |
| 46 | |
| 47 final TMirror mirror; | |
| 48 final bool isPrivate; | |
| 49 /// The comment text pre-resolution. We keep this around because inherited | |
| 50 /// methods need to resolve links differently from the superclass. | |
| 51 String unresolvedComment = ''; | |
| 52 | |
| 53 Indexable(TMirror mirror) | |
| 54 : this.mirror = mirror, | |
| 55 this.isPrivate = isHidden(mirror as DeclarationSourceMirror) { | |
| 56 | |
| 57 var mirrorQualifiedName = dart2js_util.qualifiedNameOf(this.mirror); | |
| 58 | |
| 59 var map = _mirrorToDocgen.putIfAbsent(mirrorQualifiedName, | |
| 60 () => new HashMap<String, Indexable>()); | |
| 61 | |
| 62 var added = false; | |
| 63 map.putIfAbsent(owner.docName, () { | |
| 64 added = true; | |
| 65 return this; | |
| 66 }); | |
| 67 | |
| 68 if (!added) { | |
| 69 throw new StateError('An indexable has already been stored for ' | |
| 70 '${owner.docName}'); | |
| 71 } | |
| 72 } | |
| 73 | |
| 74 /// Returns this object's qualified name, but following the conventions | |
| 75 /// we're using in Dartdoc, which is that library names with dots in them | |
| 76 /// have them replaced with hyphens. | |
| 77 String get docName; | |
| 78 | |
| 79 /// Converts all [foo] references in comments to <a>libraryName.foo</a>. | |
| 80 markdown.Node fixReference(String name) { | |
| 81 // Attempt the look up the whole name up in the scope. | |
| 82 String elementName = findElementInScope(name); | |
| 83 if (elementName != null) { | |
| 84 return new markdown.Element.text('a', elementName); | |
| 85 } | |
| 86 return fixComplexReference(name); | |
| 87 } | |
| 88 | |
| 89 /// Look for the specified name starting with the current member, and | |
| 90 /// progressively working outward to the current library scope. | |
| 91 String findElementInScope(String name) => | |
| 92 findElementInScopeWithPrefix(name, packagePrefix); | |
| 93 | |
| 94 /// The full docName of the owner element, appended with a '.' for this | |
| 95 /// object's name to be appended. | |
| 96 String get ownerPrefix => owner.docName != '' ? owner.docName + '.' : ''; | |
| 97 | |
| 98 /// The prefix String to refer to the package that this item is in, for URLs | |
| 99 /// and comment resolution. | |
| 100 /// | |
| 101 /// The prefix can be prepended to a qualified name to get a fully unique | |
| 102 /// name among all packages. | |
| 103 String get packagePrefix; | |
| 104 | |
| 105 /// Documentation comment with converted markdown and all links resolved. | |
| 106 String commentField; | |
| 107 | |
| 108 /// Accessor to documentation comment with markdown converted to html and all | |
| 109 /// links resolved. | |
| 110 String get comment { | |
| 111 if (commentField != null) return commentField; | |
| 112 | |
| 113 commentField = commentToHtml(); | |
| 114 if (commentField.isEmpty) { | |
| 115 commentField = getMdnComment(); | |
| 116 } | |
| 117 return commentField; | |
| 118 } | |
| 119 | |
| 120 void set comment(x) { | |
| 121 commentField = x; | |
| 122 } | |
| 123 | |
| 124 /// The simple name to refer to this item. | |
| 125 String get name => dart2js_util.nameOf(mirror); | |
| 126 | |
| 127 /// Accessor to the parent item that owns this item. | |
| 128 /// | |
| 129 /// "Owning" is defined as the object one scope-level above which this item | |
| 130 /// is defined. Ex: The owner for a top level class, would be its enclosing | |
| 131 /// library. The owner of a local variable in a method would be the enclosing | |
| 132 /// method. | |
| 133 Indexable get owner; | |
| 134 | |
| 135 /// Generates MDN comments from database.json. | |
| 136 String getMdnComment(); | |
| 137 | |
| 138 /// The type of this member to be used in index.txt. | |
| 139 String get typeName; | |
| 140 | |
| 141 /// Creates a [Map] with this [Indexable]'s name and a preview comment. | |
| 142 Map get previewMap { | |
| 143 var finalMap = { 'name' : name, 'qualifiedName' : qualifiedName }; | |
| 144 var pre = preview; | |
| 145 if (pre != null) finalMap['preview'] = pre; | |
| 146 return finalMap; | |
| 147 } | |
| 148 | |
| 149 String get preview { | |
| 150 if (comment != '') { | |
| 151 var index = comment.indexOf('</p>'); | |
| 152 return index > 0 ? | |
| 153 '${comment.substring(0, index)}</p>' : | |
| 154 '<p><i>Comment preview not available</i></p>'; | |
| 155 } | |
| 156 return null; | |
| 157 } | |
| 158 | |
| 159 /// Accessor to obtain the raw comment text for a given item, _without_ any | |
| 160 /// of the links resolved. | |
| 161 String get _commentText { | |
| 162 String commentText; | |
| 163 mirror.metadata.forEach((metadata) { | |
| 164 if (metadata is CommentInstanceMirror) { | |
| 165 CommentInstanceMirror comment = metadata; | |
| 166 if (comment.isDocComment) { | |
| 167 if (commentText == null) { | |
| 168 commentText = comment.trimmedText; | |
| 169 } else { | |
| 170 commentText = '$commentText\n${comment.trimmedText}'; | |
| 171 } | |
| 172 } | |
| 173 } | |
| 174 }); | |
| 175 return commentText; | |
| 176 } | |
| 177 | |
| 178 /// Returns any documentation comments associated with a mirror with | |
| 179 /// simple markdown converted to html. | |
| 180 /// | |
| 181 /// By default we resolve any comment references within our own scope. | |
| 182 /// However, if a method is inherited, we want the inherited comments, but | |
| 183 /// links to the subclasses's version of the methods. | |
| 184 String commentToHtml([Indexable resolvingScope]) { | |
| 185 if (resolvingScope == null) resolvingScope = this; | |
| 186 var commentText = _commentText; | |
| 187 unresolvedComment = commentText; | |
| 188 | |
| 189 commentText = commentText == null ? '' : | |
| 190 markdown.markdownToHtml(commentText.trim(), | |
| 191 linkResolver: resolvingScope.fixReference, | |
| 192 inlineSyntaxes: MARKDOWN_SYNTAXES); | |
| 193 return commentText; | |
| 194 } | |
| 195 | |
| 196 /// Return a map representation of this type. | |
| 197 Map toMap(); | |
| 198 | |
| 199 /// Accessor to determine if this item and all of its owners are visible. | |
| 200 bool get isVisible => isFullChainVisible(this); | |
| 201 | |
| 202 /// Returns true if [mirror] is the correct type of mirror that this Docgen | |
| 203 /// object wraps. (Workaround for the fact that Types are not first class.) | |
| 204 bool isValidMirror(DeclarationMirror mirror); | |
| 205 } | |
| 206 | |
| 207 /// Index of all the dart2js mirrors examined to corresponding MirrorBased | |
| 208 /// docgen objects. | |
| 209 /// | |
| 210 /// Used for lookup because of the dart2js mirrors exports | |
| 211 /// issue. The second level map is indexed by owner docName for faster lookup. | |
| 212 /// Why two levels of lookup? Speed, man. Speed. | |
| 213 final Map<String, Map<String, Indexable>> _mirrorToDocgen = | |
| 214 new HashMap<String, Map<String, Indexable>>(); | |
| 215 | |
| 216 Iterable<Indexable> get allIndexables => | |
| 217 _mirrorToDocgen.values.expand((map) => map.values); | |
| 218 | |
| 219 Map<String, Indexable> lookupIndexableMap(DeclarationMirror mirror) { | |
| 220 return _mirrorToDocgen[dart2js_util.qualifiedNameOf(mirror)]; | |
| 221 } | |
| OLD | NEW |