| 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.library_helpers; |
| 6 |
| 7 import 'package:logging/logging.dart'; |
| 8 import 'package:markdown/markdown.dart' as markdown; |
| 9 |
| 10 import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/source_mir
rors.dart'; |
| 11 import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors_ut
il.dart' |
| 12 as dart2js_util; |
| 13 |
| 14 import 'models.dart'; |
| 15 |
| 16 typedef DeclarationMirror LookupFunction(DeclarationSourceMirror declaration, |
| 17 String name); |
| 18 |
| 19 /// Support for [:foo:]-style code comments to the markdown parser. |
| 20 final List<markdown.InlineSyntax> MARKDOWN_SYNTAXES = |
| 21 [new markdown.CodeSyntax(r'\[:\s?((?:.|\n)*?)\s?:\]')]; |
| 22 |
| 23 bool get includePrivateMembers { |
| 24 if (_includePrivate == null) { |
| 25 throw new StateError('includePrivate has not been set'); |
| 26 } |
| 27 return _includePrivate; |
| 28 } |
| 29 |
| 30 void set includePrivateMembers(bool value) { |
| 31 if (_includePrivate != null) { |
| 32 throw new StateError('includePrivate has already been set'); |
| 33 } |
| 34 if (value == null) throw new ArgumentError('includePrivate cannot be null'); |
| 35 _includePrivate = value; |
| 36 } |
| 37 |
| 38 bool _includePrivate; |
| 39 |
| 40 /// Return true if this item and all of its owners are all visible. |
| 41 bool isFullChainVisible(Indexable item) { |
| 42 return includePrivateMembers || (!item.isPrivate && (item.owner != null ? |
| 43 isFullChainVisible(item.owner) : true)); |
| 44 } |
| 45 |
| 46 /// Logger for printing out progress of documentation generation. |
| 47 final Logger logger = new Logger('Docgen'); |
| 48 |
| 49 /// The dart:core library, which contains all types that are always available |
| 50 /// without import. |
| 51 Library _coreLibrary; |
| 52 |
| 53 /// Set of libraries declared in the SDK, so libraries that can be accessed |
| 54 /// when running dart by default. |
| 55 Iterable<LibraryMirror> get sdkLibraries => _sdkLibraries; |
| 56 Iterable<LibraryMirror> _sdkLibraries; |
| 57 |
| 58 /// Index of all the dart2js mirrors examined to corresponding MirrorBased |
| 59 /// docgen objects. |
| 60 /// |
| 61 /// Used for lookup because of the dart2js mirrors exports |
| 62 /// issue. The second level map is indexed by owner docName for faster lookup. |
| 63 /// Why two levels of lookup? Speed, man. Speed. |
| 64 final Map<String, Map<String, Set<Indexable>>> mirrorToDocgen = new Map<String, |
| 65 Map<String, Set<Indexable>>>(); |
| 66 |
| 67 ////// Top level resolution functions |
| 68 /// Converts all [foo] references in comments to <a>libraryName.foo</a>. |
| 69 markdown.Node globalFixReference(String name) { |
| 70 // Attempt the look up the whole name up in the scope. |
| 71 String elementName = findElementInScopeWithPrefix(name, ''); |
| 72 if (elementName != null) { |
| 73 return new markdown.Element.text('a', elementName); |
| 74 } |
| 75 return fixComplexReference(name); |
| 76 } |
| 77 |
| 78 /// This is a more complex reference. Try to break up if its of the form A<B> |
| 79 /// where A is an alphanumeric string and B is an A, a list of B ("B, B, B"), |
| 80 /// or of the form A<B>. Note: unlike other the other markdown-style links, |
| 81 /// all text inside the square brackets is treated as part of the link (aka |
| 82 /// the * is interpreted literally as a *, not as a indicator for bold <em>. |
| 83 /// |
| 84 /// Example: [foo<_bar_>] will produce |
| 85 /// <a>resolvedFoo</a><<a>resolved_bar_</a>> rather than an italicized |
| 86 /// version of resolvedBar. |
| 87 markdown.Node fixComplexReference(String name) { |
| 88 // Parse into multiple elements we can try to resolve. |
| 89 var tokens = _tokenizeComplexReference(name); |
| 90 |
| 91 // Produce an html representation of our elements. Group unresolved and |
| 92 // plain text are grouped into "link" elements so they display as code. |
| 93 final textElements = [' ', ',', '>', _LESS_THAN]; |
| 94 var accumulatedHtml = ''; |
| 95 |
| 96 for (var token in tokens) { |
| 97 bool added = false; |
| 98 if (!textElements.contains(token)) { |
| 99 String elementName = findElementInScopeWithPrefix(token, ''); |
| 100 if (elementName != null) { |
| 101 accumulatedHtml += markdown.renderToHtml([new markdown.Element.text('a', |
| 102 elementName)]); |
| 103 added = true; |
| 104 } |
| 105 } |
| 106 if (!added) { |
| 107 accumulatedHtml += token; |
| 108 } |
| 109 } |
| 110 return new markdown.Text(accumulatedHtml); |
| 111 } |
| 112 |
| 113 String findElementInScopeWithPrefix(String name, String packagePrefix) { |
| 114 var lookupFunc = determineLookupFunc(name); |
| 115 // Look in the dart core library scope. |
| 116 var coreScope = _coreLibrary == null ? null : lookupFunc(_coreLibrary.mirror, |
| 117 name); |
| 118 if (coreScope != null) return packagePrefix + _coreLibrary.docName; |
| 119 |
| 120 // If it's a reference that starts with a another library name, then it |
| 121 // looks for a match of that library name in the other sdk libraries. |
| 122 if (name.contains('.')) { |
| 123 var index = name.indexOf('.'); |
| 124 var libraryName = name.substring(0, index); |
| 125 var remainingName = name.substring(index + 1); |
| 126 foundLibraryName(library) => library.uri.pathSegments[0] == libraryName; |
| 127 |
| 128 if (_sdkLibraries.any(foundLibraryName)) { |
| 129 var library = _sdkLibraries.singleWhere(foundLibraryName); |
| 130 // Look to see if it's a fully qualified library name. |
| 131 var scope = determineLookupFunc(remainingName)(library, remainingName); |
| 132 if (scope != null) { |
| 133 var result = getDocgenObject(scope); |
| 134 if (result is DummyMirror) { |
| 135 return packagePrefix + result.docName; |
| 136 } else { |
| 137 return result.packagePrefix + result.docName; |
| 138 } |
| 139 } |
| 140 } |
| 141 } |
| 142 return null; |
| 143 } |
| 144 |
| 145 /// Given a Dart2jsMirror, find the corresponding Docgen [MirrorBased] object. |
| 146 /// |
| 147 /// We have this global lookup function to avoid re-implementing looking up |
| 148 /// the scoping rules for comment resolution here (it is currently done in |
| 149 /// mirrors). If no corresponding MirrorBased object is found, we return a |
| 150 /// [DummyMirror] that simply returns the original mirror's qualifiedName |
| 151 /// while behaving like a MirrorBased object. |
| 152 Indexable getDocgenObject(DeclarationMirror mirror, [Indexable owner]) { |
| 153 Map<String, Set<Indexable>> docgenObj = |
| 154 mirrorToDocgen[dart2js_util.qualifiedNameOf(mirror)]; |
| 155 if (docgenObj == null) { |
| 156 return new DummyMirror(mirror, owner); |
| 157 } |
| 158 |
| 159 var setToExamine = new Set(); |
| 160 if (owner != null) { |
| 161 var firstSet = docgenObj[owner.docName]; |
| 162 if (firstSet != null) setToExamine.addAll(firstSet); |
| 163 if (_coreLibrary != null && docgenObj[_coreLibrary.docName] != null) { |
| 164 setToExamine.addAll(docgenObj[_coreLibrary.docName]); |
| 165 } |
| 166 } else { |
| 167 for (var value in docgenObj.values) { |
| 168 setToExamine.addAll(value); |
| 169 } |
| 170 } |
| 171 |
| 172 Set<Indexable> results = new Set<Indexable>(); |
| 173 for (Indexable indexable in setToExamine) { |
| 174 if (indexable.mirror.qualifiedName == mirror.qualifiedName && |
| 175 indexable.isValidMirror(mirror)) { |
| 176 results.add(indexable); |
| 177 } |
| 178 } |
| 179 |
| 180 if (results.length > 0) { |
| 181 // This might occur if we didn't specify an "owner." |
| 182 return results.first; |
| 183 } |
| 184 return new DummyMirror(mirror, owner); |
| 185 } |
| 186 |
| 187 void initializeTopLevelLibraries(MirrorSystem mirrorSystem) { |
| 188 _sdkLibraries = mirrorSystem.libraries.values.where( |
| 189 (each) => each.uri.scheme == 'dart'); |
| 190 _coreLibrary = new Library(_sdkLibraries.singleWhere((lib) => |
| 191 lib.uri.toString().startsWith('dart:core'))); |
| 192 } |
| 193 |
| 194 /// For a given name, determine if we need to resolve it as a qualified name |
| 195 /// or a simple name in the source mirors. |
| 196 LookupFunction determineLookupFunc(String name) => name.contains('.') ? |
| 197 dart2js_util.lookupQualifiedInScope : |
| 198 (mirror, name) => mirror.lookupInScope(name); |
| 199 |
| 200 /// Chunk the provided name into individual parts to be resolved. We take a |
| 201 /// simplistic approach to chunking, though, we break at " ", ",", "<" |
| 202 /// and ">". All other characters are grouped into the name to be resolved. |
| 203 /// As a result, these characters will all be treated as part of the item to |
| 204 /// be resolved (aka the * is interpreted literally as a *, not as an |
| 205 /// indicator for bold <em>. |
| 206 List<String> _tokenizeComplexReference(String name) { |
| 207 var tokens = []; |
| 208 var append = false; |
| 209 var index = 0; |
| 210 while (index < name.length) { |
| 211 if (name.indexOf(_LESS_THAN, index) == index) { |
| 212 tokens.add(_LESS_THAN); |
| 213 append = false; |
| 214 index += _LESS_THAN.length; |
| 215 } else if (name[index] == ' ' || name[index] == ',' || name[index] == '>') { |
| 216 tokens.add(name[index]); |
| 217 append = false; |
| 218 index++; |
| 219 } else { |
| 220 if (append) { |
| 221 tokens[tokens.length - 1] = tokens.last + name[index]; |
| 222 } else { |
| 223 tokens.add(name[index]); |
| 224 append = true; |
| 225 } |
| 226 index++; |
| 227 } |
| 228 } |
| 229 return tokens; |
| 230 } |
| 231 |
| 232 // HTML escaped version of '<' character. |
| 233 const _LESS_THAN = '<'; |
| OLD | NEW |