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 |