| Index: pkg/docgen/lib/src/library_helpers.dart
|
| diff --git a/pkg/docgen/lib/src/library_helpers.dart b/pkg/docgen/lib/src/library_helpers.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..3bb56726420c63982a3c37025b9ebf048f7b6d0a
|
| --- /dev/null
|
| +++ b/pkg/docgen/lib/src/library_helpers.dart
|
| @@ -0,0 +1,233 @@
|
| +// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
| +// for details. All rights reserved. Use of this source code is governed by a
|
| +// BSD-style license that can be found in the LICENSE file.
|
| +
|
| +library docgen.library_helpers;
|
| +
|
| +import 'package:logging/logging.dart';
|
| +import 'package:markdown/markdown.dart' as markdown;
|
| +
|
| +import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/source_mirrors.dart';
|
| +import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util.dart'
|
| + as dart2js_util;
|
| +
|
| +import 'models.dart';
|
| +
|
| +typedef DeclarationMirror LookupFunction(DeclarationSourceMirror declaration,
|
| + String name);
|
| +
|
| +/// Support for [:foo:]-style code comments to the markdown parser.
|
| +final List<markdown.InlineSyntax> MARKDOWN_SYNTAXES =
|
| + [new markdown.CodeSyntax(r'\[:\s?((?:.|\n)*?)\s?:\]')];
|
| +
|
| +bool get includePrivateMembers {
|
| + if (_includePrivate == null) {
|
| + throw new StateError('includePrivate has not been set');
|
| + }
|
| + return _includePrivate;
|
| +}
|
| +
|
| +void set includePrivateMembers(bool value) {
|
| + if (_includePrivate != null) {
|
| + throw new StateError('includePrivate has already been set');
|
| + }
|
| + if (value == null) throw new ArgumentError('includePrivate cannot be null');
|
| + _includePrivate = value;
|
| +}
|
| +
|
| +bool _includePrivate;
|
| +
|
| +/// Return true if this item and all of its owners are all visible.
|
| +bool isFullChainVisible(Indexable item) {
|
| + return includePrivateMembers || (!item.isPrivate && (item.owner != null ?
|
| + isFullChainVisible(item.owner) : true));
|
| +}
|
| +
|
| +/// Logger for printing out progress of documentation generation.
|
| +final Logger logger = new Logger('Docgen');
|
| +
|
| +/// The dart:core library, which contains all types that are always available
|
| +/// without import.
|
| +Library _coreLibrary;
|
| +
|
| +/// Set of libraries declared in the SDK, so libraries that can be accessed
|
| +/// when running dart by default.
|
| +Iterable<LibraryMirror> get sdkLibraries => _sdkLibraries;
|
| +Iterable<LibraryMirror> _sdkLibraries;
|
| +
|
| +/// Index of all the dart2js mirrors examined to corresponding MirrorBased
|
| +/// docgen objects.
|
| +///
|
| +/// Used for lookup because of the dart2js mirrors exports
|
| +/// issue. The second level map is indexed by owner docName for faster lookup.
|
| +/// Why two levels of lookup? Speed, man. Speed.
|
| +final Map<String, Map<String, Set<Indexable>>> mirrorToDocgen = new Map<String,
|
| + Map<String, Set<Indexable>>>();
|
| +
|
| +////// Top level resolution functions
|
| +/// Converts all [foo] references in comments to <a>libraryName.foo</a>.
|
| +markdown.Node globalFixReference(String name) {
|
| + // Attempt the look up the whole name up in the scope.
|
| + String elementName = findElementInScopeWithPrefix(name, '');
|
| + if (elementName != null) {
|
| + return new markdown.Element.text('a', elementName);
|
| + }
|
| + return fixComplexReference(name);
|
| +}
|
| +
|
| +/// This is a more complex reference. Try to break up if its of the form A<B>
|
| +/// where A is an alphanumeric string and B is an A, a list of B ("B, B, B"),
|
| +/// or of the form A<B>. Note: unlike other the other markdown-style links,
|
| +/// all text inside the square brackets is treated as part of the link (aka
|
| +/// the * is interpreted literally as a *, not as a indicator for bold <em>.
|
| +///
|
| +/// Example: [foo<_bar_>] will produce
|
| +/// <a>resolvedFoo</a><<a>resolved_bar_</a>> rather than an italicized
|
| +/// version of resolvedBar.
|
| +markdown.Node fixComplexReference(String name) {
|
| + // Parse into multiple elements we can try to resolve.
|
| + var tokens = _tokenizeComplexReference(name);
|
| +
|
| + // Produce an html representation of our elements. Group unresolved and
|
| + // plain text are grouped into "link" elements so they display as code.
|
| + final textElements = [' ', ',', '>', _LESS_THAN];
|
| + var accumulatedHtml = '';
|
| +
|
| + for (var token in tokens) {
|
| + bool added = false;
|
| + if (!textElements.contains(token)) {
|
| + String elementName = findElementInScopeWithPrefix(token, '');
|
| + if (elementName != null) {
|
| + accumulatedHtml += markdown.renderToHtml([new markdown.Element.text('a',
|
| + elementName)]);
|
| + added = true;
|
| + }
|
| + }
|
| + if (!added) {
|
| + accumulatedHtml += token;
|
| + }
|
| + }
|
| + return new markdown.Text(accumulatedHtml);
|
| +}
|
| +
|
| +String findElementInScopeWithPrefix(String name, String packagePrefix) {
|
| + var lookupFunc = determineLookupFunc(name);
|
| + // Look in the dart core library scope.
|
| + var coreScope = _coreLibrary == null ? null : lookupFunc(_coreLibrary.mirror,
|
| + name);
|
| + if (coreScope != null) return packagePrefix + _coreLibrary.docName;
|
| +
|
| + // If it's a reference that starts with a another library name, then it
|
| + // looks for a match of that library name in the other sdk libraries.
|
| + if (name.contains('.')) {
|
| + var index = name.indexOf('.');
|
| + var libraryName = name.substring(0, index);
|
| + var remainingName = name.substring(index + 1);
|
| + foundLibraryName(library) => library.uri.pathSegments[0] == libraryName;
|
| +
|
| + if (_sdkLibraries.any(foundLibraryName)) {
|
| + var library = _sdkLibraries.singleWhere(foundLibraryName);
|
| + // Look to see if it's a fully qualified library name.
|
| + var scope = determineLookupFunc(remainingName)(library, remainingName);
|
| + if (scope != null) {
|
| + var result = getDocgenObject(scope);
|
| + if (result is DummyMirror) {
|
| + return packagePrefix + result.docName;
|
| + } else {
|
| + return result.packagePrefix + result.docName;
|
| + }
|
| + }
|
| + }
|
| + }
|
| + return null;
|
| +}
|
| +
|
| +/// Given a Dart2jsMirror, find the corresponding Docgen [MirrorBased] object.
|
| +///
|
| +/// We have this global lookup function to avoid re-implementing looking up
|
| +/// the scoping rules for comment resolution here (it is currently done in
|
| +/// mirrors). If no corresponding MirrorBased object is found, we return a
|
| +/// [DummyMirror] that simply returns the original mirror's qualifiedName
|
| +/// while behaving like a MirrorBased object.
|
| +Indexable getDocgenObject(DeclarationMirror mirror, [Indexable owner]) {
|
| + Map<String, Set<Indexable>> docgenObj =
|
| + mirrorToDocgen[dart2js_util.qualifiedNameOf(mirror)];
|
| + if (docgenObj == null) {
|
| + return new DummyMirror(mirror, owner);
|
| + }
|
| +
|
| + var setToExamine = new Set();
|
| + if (owner != null) {
|
| + var firstSet = docgenObj[owner.docName];
|
| + if (firstSet != null) setToExamine.addAll(firstSet);
|
| + if (_coreLibrary != null && docgenObj[_coreLibrary.docName] != null) {
|
| + setToExamine.addAll(docgenObj[_coreLibrary.docName]);
|
| + }
|
| + } else {
|
| + for (var value in docgenObj.values) {
|
| + setToExamine.addAll(value);
|
| + }
|
| + }
|
| +
|
| + Set<Indexable> results = new Set<Indexable>();
|
| + for (Indexable indexable in setToExamine) {
|
| + if (indexable.mirror.qualifiedName == mirror.qualifiedName &&
|
| + indexable.isValidMirror(mirror)) {
|
| + results.add(indexable);
|
| + }
|
| + }
|
| +
|
| + if (results.length > 0) {
|
| + // This might occur if we didn't specify an "owner."
|
| + return results.first;
|
| + }
|
| + return new DummyMirror(mirror, owner);
|
| +}
|
| +
|
| +void initializeTopLevelLibraries(MirrorSystem mirrorSystem) {
|
| + _sdkLibraries = mirrorSystem.libraries.values.where(
|
| + (each) => each.uri.scheme == 'dart');
|
| + _coreLibrary = new Library(_sdkLibraries.singleWhere((lib) =>
|
| + lib.uri.toString().startsWith('dart:core')));
|
| +}
|
| +
|
| +/// For a given name, determine if we need to resolve it as a qualified name
|
| +/// or a simple name in the source mirors.
|
| +LookupFunction determineLookupFunc(String name) => name.contains('.') ?
|
| + dart2js_util.lookupQualifiedInScope :
|
| + (mirror, name) => mirror.lookupInScope(name);
|
| +
|
| +/// Chunk the provided name into individual parts to be resolved. We take a
|
| +/// simplistic approach to chunking, though, we break at " ", ",", "<"
|
| +/// and ">". All other characters are grouped into the name to be resolved.
|
| +/// As a result, these characters will all be treated as part of the item to
|
| +/// be resolved (aka the * is interpreted literally as a *, not as an
|
| +/// indicator for bold <em>.
|
| +List<String> _tokenizeComplexReference(String name) {
|
| + var tokens = [];
|
| + var append = false;
|
| + var index = 0;
|
| + while (index < name.length) {
|
| + if (name.indexOf(_LESS_THAN, index) == index) {
|
| + tokens.add(_LESS_THAN);
|
| + append = false;
|
| + index += _LESS_THAN.length;
|
| + } else if (name[index] == ' ' || name[index] == ',' || name[index] == '>') {
|
| + tokens.add(name[index]);
|
| + append = false;
|
| + index++;
|
| + } else {
|
| + if (append) {
|
| + tokens[tokens.length - 1] = tokens.last + name[index];
|
| + } else {
|
| + tokens.add(name[index]);
|
| + append = true;
|
| + }
|
| + index++;
|
| + }
|
| + }
|
| + return tokens;
|
| +}
|
| +
|
| +// HTML escaped version of '<' character.
|
| +const _LESS_THAN = '<';
|
|
|