| Index: utils/dartdoc/dartdoc.dart | 
| diff --git a/utils/dartdoc/dartdoc.dart b/utils/dartdoc/dartdoc.dart | 
| deleted file mode 100644 | 
| index 417234949756f9bc645b8045a65965ef773379be..0000000000000000000000000000000000000000 | 
| --- a/utils/dartdoc/dartdoc.dart | 
| +++ /dev/null | 
| @@ -1,1218 +0,0 @@ | 
| -// Copyright (c) 2011, 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. | 
| - | 
| -/** | 
| - * To use it, from this directory, run: | 
| - * | 
| - *     $ ./dartdoc <path to .dart file> | 
| - * | 
| - * This will create a "docs" directory with the docs for your libraries. To | 
| - * create these beautiful docs, dartdoc parses your library and every library | 
| - * it imports (recursively). From each library, it parses all classes and | 
| - * members, finds the associated doc comments and builds crosslinked docs from | 
| - * them. | 
| - */ | 
| -#library('dartdoc'); | 
| - | 
| -#import('dart:io'); | 
| -#import('dart:json'); | 
| -#import('../../frog/lang.dart'); | 
| -#import('../../frog/file_system.dart'); | 
| -#import('../../frog/file_system_vm.dart'); | 
| -#import('classify.dart'); | 
| -#import('markdown.dart', prefix: 'md'); | 
| - | 
| -#source('comment_map.dart'); | 
| -#source('utils.dart'); | 
| - | 
| -/** Path to generate HTML files into. */ | 
| -final _outdir = 'docs'; | 
| - | 
| -/** | 
| - * Generates completely static HTML containing everything you need to browse | 
| - * the docs. The only client side behavior is trivial stuff like syntax | 
| - * highlighting code. | 
| - */ | 
| -final MODE_STATIC = 0; | 
| - | 
| -/** | 
| - * Generated docs do not include baked HTML navigation. Instead, a single | 
| - * `nav.json` file is created and the appropriate navigation is generated | 
| - * client-side by parsing that and building HTML. | 
| - * | 
| - * This dramatically reduces the generated size of the HTML since a large | 
| - * fraction of each static page is just redundant navigation links. | 
| - * | 
| - * In this mode, the browser will do a XHR for nav.json which means that to | 
| - * preview docs locally, you will need to enable requesting file:// links in | 
| - * your browser or run a little local server like `python -m SimpleHTTPServer`. | 
| - */ | 
| -final MODE_LIVE_NAV = 1; | 
| - | 
| -/** | 
| - * Run this from the `utils/dartdoc` directory. | 
| - */ | 
| -void main() { | 
| -  final args = new Options().arguments; | 
| - | 
| -  // The entrypoint of the library to generate docs for. | 
| -  final entrypoint = args[args.length - 1]; | 
| - | 
| -  // Parse the dartdoc options. | 
| -  bool includeSource = true; | 
| -  var mode = MODE_LIVE_NAV; | 
| - | 
| -  for (int i = 2; i < args.length - 1; i++) { | 
| -    final arg = args[i]; | 
| -    switch (arg) { | 
| -      case '--no-code': | 
| -        includeSource = false; | 
| -        break; | 
| - | 
| -      case '--mode=static': | 
| -        mode = MODE_STATIC; | 
| -        break; | 
| - | 
| -      case '--mode=live-nav': | 
| -        mode = MODE_LIVE_NAV; | 
| -        break; | 
| - | 
| -      default: | 
| -        print('Unknown option: $arg'); | 
| -    } | 
| -  } | 
| - | 
| -  final files = new VMFileSystem(); | 
| -  parseOptions('../../frog', ['', '', '--libdir=../../frog/lib'], files); | 
| -  initializeWorld(files); | 
| - | 
| -  var dartdoc; | 
| -  final elapsed = time(() { | 
| -    dartdoc = new Dartdoc(); | 
| -    dartdoc.includeSource = includeSource; | 
| -    dartdoc.mode = mode; | 
| - | 
| -    dartdoc.document(entrypoint); | 
| -  }); | 
| - | 
| -  print('Documented ${dartdoc._totalLibraries} libraries, ' + | 
| -      '${dartdoc._totalTypes} types, and ' + | 
| -      '${dartdoc._totalMembers} members in ${elapsed}msec.'); | 
| -} | 
| - | 
| -class Dartdoc { | 
| -  /** Set to `false` to not include the source code in the generated docs. */ | 
| -  bool includeSource = true; | 
| - | 
| -  /** | 
| -   * Dartdoc can generate docs in a few different ways based on how dynamic you | 
| -   * want the client-side behavior to be. The value for this should be one of | 
| -   * the `MODE_` constants. | 
| -   */ | 
| -  int mode = MODE_LIVE_NAV; | 
| - | 
| -  /** | 
| -   * The title used for the overall generated output. Set this to change it. | 
| -   */ | 
| -  String mainTitle = 'Dart Documentation'; | 
| - | 
| -  /** | 
| -   * The URL that the Dart logo links to. Defaults "index.html", the main | 
| -   * page for the generated docs, but can be anything. | 
| -   */ | 
| -  String mainUrl = 'index.html'; | 
| - | 
| -  /** | 
| -   * The Google Custom Search ID that should be used for the search box. If | 
| -   * this is `null` then no search box will be shown. | 
| -   */ | 
| -  String searchEngineId = null; | 
| - | 
| -  /* The URL that the embedded search results should be displayed on. */ | 
| -  String searchResultsUrl = 'results.html'; | 
| - | 
| -  /** Set this to add footer text to each generated page. */ | 
| -  String footerText = ''; | 
| - | 
| -  /** | 
| -   * From exposes the set of libraries in `world.libraries`. That maps library | 
| -   * *keys* to [Library] objects. The keys are *not* exactly the same as their | 
| -   * names. This means if we order by key, we won't actually have them sorted | 
| -   * correctly. This list contains the libraries in correct order by their | 
| -   * *name*. | 
| -   */ | 
| -  List<Library> _sortedLibraries; | 
| - | 
| -  CommentMap _comments; | 
| - | 
| -  /** The library that we're currently generating docs for. */ | 
| -  Library _currentLibrary; | 
| - | 
| -  /** The type that we're currently generating docs for. */ | 
| -  Type _currentType; | 
| - | 
| -  /** The member that we're currently generating docs for. */ | 
| -  Member _currentMember; | 
| - | 
| -  /** The path to the file currently being written to, relative to [outdir]. */ | 
| -  String _filePath; | 
| - | 
| -  /** The file currently being written to. */ | 
| -  StringBuffer _file; | 
| - | 
| -  int _totalLibraries = 0; | 
| -  int _totalTypes = 0; | 
| -  int _totalMembers = 0; | 
| - | 
| -  Dartdoc() | 
| -    : _comments = new CommentMap() { | 
| -    // Patch in support for [:...:]-style code to the markdown parser. | 
| -    // TODO(rnystrom): Markdown already has syntax for this. Phase this out? | 
| -    md.InlineParser.syntaxes.insertRange(0, 1, | 
| -        new md.CodeSyntax(@'\[\:((?:.|\n)*?)\:\]')); | 
| - | 
| -    md.setImplicitLinkResolver((name) => resolveNameReference(name, | 
| -            library: _currentLibrary, type: _currentType, | 
| -            member: _currentMember)); | 
| -  } | 
| - | 
| -  void document(String entrypoint) { | 
| -    var oldDietParse = options.dietParse; | 
| -    try { | 
| -      options.dietParse = true; | 
| - | 
| -      // Handle the built-in entrypoints. | 
| -      switch (entrypoint) { | 
| -        case 'corelib': | 
| -          world.getOrAddLibrary('dart:core'); | 
| -          world.getOrAddLibrary('dart:coreimpl'); | 
| -          world.getOrAddLibrary('dart:json'); | 
| -          world.getOrAddLibrary('dart:isolate'); | 
| -          world.process(); | 
| -          break; | 
| - | 
| -        case 'dom': | 
| -          world.getOrAddLibrary('dart:core'); | 
| -          world.getOrAddLibrary('dart:coreimpl'); | 
| -          world.getOrAddLibrary('dart:json'); | 
| -          world.getOrAddLibrary('dart:dom'); | 
| -          world.getOrAddLibrary('dart:isolate'); | 
| -          world.process(); | 
| -          break; | 
| - | 
| -        case 'html': | 
| -          world.getOrAddLibrary('dart:core'); | 
| -          world.getOrAddLibrary('dart:coreimpl'); | 
| -          world.getOrAddLibrary('dart:json'); | 
| -          world.getOrAddLibrary('dart:dom'); | 
| -          world.getOrAddLibrary('dart:html'); | 
| -          world.getOrAddLibrary('dart:isolate'); | 
| -          world.process(); | 
| -          break; | 
| - | 
| -        default: | 
| -          // Normal entrypoint script. | 
| -          world.processDartScript(entrypoint); | 
| -      } | 
| - | 
| -      world.resolveAll(); | 
| - | 
| -      // Sort the libraries by name (not key). | 
| -      _sortedLibraries = world.libraries.getValues(); | 
| -      _sortedLibraries.sort((a, b) { | 
| -        return a.name.toUpperCase().compareTo(b.name.toUpperCase()); | 
| -      }); | 
| - | 
| -      // Generate the docs. | 
| -      if (mode == MODE_LIVE_NAV) docNavigationJson(); | 
| - | 
| -      docIndex(); | 
| -      for (final library in _sortedLibraries) { | 
| -        docLibrary(library); | 
| -      } | 
| -    } finally { | 
| -      options.dietParse = oldDietParse; | 
| -    } | 
| -  } | 
| - | 
| -  void startFile(String path) { | 
| -    _filePath = path; | 
| -    _file = new StringBuffer(); | 
| -  } | 
| - | 
| -  void endFile() { | 
| -    final outPath = '$_outdir/$_filePath'; | 
| -    final dir = new Directory(dirname(outPath)); | 
| -    if (!dir.existsSync()) { | 
| -      dir.createSync(); | 
| -    } | 
| - | 
| -    world.files.writeString(outPath, _file.toString()); | 
| -    _filePath = null; | 
| -    _file = null; | 
| -  } | 
| - | 
| -  void write(String s) { | 
| -    _file.add(s); | 
| -  } | 
| - | 
| -  void writeln(String s) { | 
| -    write(s); | 
| -    write('\n'); | 
| -  } | 
| - | 
| -  /** | 
| -   * Writes the page header with the given [title] and [breadcrumbs]. The | 
| -   * breadcrumbs are an interleaved list of links and titles. If a link is null, | 
| -   * then no link will be generated. For example, given: | 
| -   * | 
| -   *     ['foo', 'foo.html', 'bar', null] | 
| -   * | 
| -   * It will output: | 
| -   * | 
| -   *     <a href="foo.html">foo</a> › bar | 
| -   */ | 
| -  void writeHeader(String title, List<String> breadcrumbs) { | 
| -    write( | 
| -        ''' | 
| -        <!DOCTYPE html> | 
| -        <html> | 
| -        <head> | 
| -        '''); | 
| -    writeHeadContents(title); | 
| - | 
| -    // Add data attributes describing what the page documents. | 
| -    var data = ''; | 
| -    if (_currentLibrary != null) { | 
| -      data += ' data-library="${md.escapeHtml(_currentLibrary.name)}"'; | 
| -    } | 
| - | 
| -    if (_currentType != null) { | 
| -      data += ' data-type="${md.escapeHtml(typeName(_currentType))}"'; | 
| -    } | 
| - | 
| -    write( | 
| -        ''' | 
| -        </head> | 
| -        <body$data> | 
| -        <div class="page"> | 
| -        <div class="header"> | 
| -          ${a(mainUrl, '<div class="logo"></div>')} | 
| -          ${a('index.html', mainTitle)} | 
| -        '''); | 
| - | 
| -    // Write the breadcrumb trail. | 
| -    for (int i = 0; i < breadcrumbs.length; i += 2) { | 
| -      if (breadcrumbs[i + 1] == null) { | 
| -        write(' › ${breadcrumbs[i]}'); | 
| -      } else { | 
| -        write(' › ${a(breadcrumbs[i + 1], breadcrumbs[i])}'); | 
| -      } | 
| -    } | 
| - | 
| -    if (searchEngineId != null) { | 
| -      writeln( | 
| -        ''' | 
| -        <form action="$searchResultsUrl" id="search-box"> | 
| -          <input type="hidden" name="cx" value="$searchEngineId"> | 
| -          <input type="hidden" name="ie" value="UTF-8"> | 
| -          <input type="hidden" name="hl" value="en"> | 
| -          <input type="search" name="q" id="q" autocomplete="off" | 
| -              placeholder="Search"> | 
| -        </form> | 
| -        '''); | 
| -    } | 
| - | 
| -    writeln('</div>'); | 
| - | 
| -    docNavigation(); | 
| -    writeln('<div class="content">'); | 
| -  } | 
| - | 
| -  String get clientScript() { | 
| -    switch (mode) { | 
| -      case MODE_STATIC:   return 'client-static'; | 
| -      case MODE_LIVE_NAV: return 'client-live-nav'; | 
| -      default: throw 'Unknown mode $mode.'; | 
| -    } | 
| -  } | 
| - | 
| -  void writeHeadContents(String title) { | 
| -    writeln( | 
| -        ''' | 
| -        <meta charset="utf-8"> | 
| -        <title>$title</title> | 
| -        <link rel="stylesheet" type="text/css" | 
| -            href="${relativePath('styles.css')}" /> | 
| -        <link href="http://fonts.googleapis.com/css?family=Open+Sans:400,600,700,800" rel="stylesheet" type="text/css"> | 
| -        <link rel="shortcut icon" href="${relativePath('favicon.ico')}" /> | 
| -        <script src="${relativePath('$clientScript.js')}"></script> | 
| -        '''); | 
| -  } | 
| - | 
| -  void writeFooter() { | 
| -    writeln( | 
| -        ''' | 
| -        </div> | 
| -        <div class="clear"></div> | 
| -        </div> | 
| -        <div class="footer">$footerText</div> | 
| -        </body></html> | 
| -        '''); | 
| -  } | 
| - | 
| -  void docIndex() { | 
| -    startFile('index.html'); | 
| - | 
| -    writeHeader(mainTitle, []); | 
| - | 
| -    writeln('<h2>$mainTitle</h2>'); | 
| -    writeln('<h3>Libraries</h3>'); | 
| - | 
| -    for (final library in _sortedLibraries) { | 
| -      docIndexLibrary(library); | 
| -    } | 
| - | 
| -    writeFooter(); | 
| -    endFile(); | 
| -  } | 
| - | 
| -  void docIndexLibrary(Library library) { | 
| -    writeln('<h4>${a(libraryUrl(library), library.name)}</h4>'); | 
| -  } | 
| - | 
| -  /** | 
| -   * Walks the libraries and creates a JSON object containing the data needed | 
| -   * to generate navigation for them. | 
| -   */ | 
| -  void docNavigationJson() { | 
| -    startFile('nav.json'); | 
| - | 
| -    final libraries = {}; | 
| - | 
| -    for (final library in _sortedLibraries) { | 
| -      docLibraryNavigationJson(library, libraries); | 
| -    } | 
| - | 
| -    writeln(JSON.stringify(libraries)); | 
| -    endFile(); | 
| -  } | 
| - | 
| -  void docLibraryNavigationJson(Library library, Map libraries) { | 
| -    final types = []; | 
| - | 
| -    for (final type in orderByName(library.types)) { | 
| -      if (type.isTop) continue; | 
| -      if (type.name.startsWith('_')) continue; | 
| - | 
| -      final kind = type.isClass ? 'class' : 'interface'; | 
| -      final url = typeUrl(type); | 
| -      types.add({ 'name': typeName(type), 'kind': kind, 'url': url }); | 
| -    } | 
| - | 
| -    libraries[library.name] = types; | 
| -  } | 
| - | 
| -  void docNavigation() { | 
| -    writeln( | 
| -        ''' | 
| -        <div class="nav"> | 
| -        '''); | 
| - | 
| -    if (mode == MODE_STATIC) { | 
| -      for (final library in _sortedLibraries) { | 
| -        write('<h2><div class="icon-library"></div>'); | 
| - | 
| -        if ((_currentLibrary == library) && (_currentType == null)) { | 
| -          write('<strong>${library.name}</strong>'); | 
| -        } else { | 
| -          write('${a(libraryUrl(library), library.name)}'); | 
| -        } | 
| -        write('</h2>'); | 
| - | 
| -        // Only expand classes in navigation for current library. | 
| -        if (_currentLibrary == library) docLibraryNavigation(library); | 
| -      } | 
| -    } | 
| - | 
| -    writeln('</div>'); | 
| -  } | 
| - | 
| -  /** Writes the navigation for the types contained by the given library. */ | 
| -  void docLibraryNavigation(Library library) { | 
| -    // Show the exception types separately. | 
| -    final types = <Type>[]; | 
| -    final exceptions = <Type>[]; | 
| - | 
| -    for (final type in orderByName(library.types)) { | 
| -      if (type.isTop) continue; | 
| -      if (type.name.startsWith('_')) continue; | 
| - | 
| -      if (type.name.endsWith('Exception')) { | 
| -        exceptions.add(type); | 
| -      } else { | 
| -        types.add(type); | 
| -      } | 
| -    } | 
| - | 
| -    if ((types.length == 0) && (exceptions.length == 0)) return; | 
| - | 
| -    writeln('<ul class="icon">'); | 
| -    types.forEach(docTypeNavigation); | 
| -    exceptions.forEach(docTypeNavigation); | 
| -    writeln('</ul>'); | 
| -  } | 
| - | 
| -  /** Writes a linked navigation list item for the given type. */ | 
| -  void docTypeNavigation(Type type) { | 
| -    var icon = 'interface'; | 
| -    if (type.name.endsWith('Exception')) { | 
| -      icon = 'exception'; | 
| -    } else if (type.isClass) { | 
| -      icon = 'class'; | 
| -    } | 
| - | 
| -    write('<li>'); | 
| -    if (_currentType == type) { | 
| -      write( | 
| -          '<div class="icon-$icon"></div><strong>${typeName(type)}</strong>'); | 
| -    } else { | 
| -      write(a(typeUrl(type), | 
| -          '<div class="icon-$icon"></div>${typeName(type)}')); | 
| -    } | 
| -    writeln('</li>'); | 
| -  } | 
| - | 
| -  void docLibrary(Library library) { | 
| -    _totalLibraries++; | 
| -    _currentLibrary = library; | 
| -    _currentType = null; | 
| - | 
| -    startFile(libraryUrl(library)); | 
| -    writeHeader(library.name, [library.name, libraryUrl(library)]); | 
| -    writeln('<h2>Library <strong>${library.name}</strong></h2>'); | 
| - | 
| -    // Look for a comment for the entire library. | 
| -    final comment = _comments.findLibrary(library.baseSource); | 
| -    if (comment != null) { | 
| -      final html = md.markdownToHtml(comment); | 
| -      writeln('<div class="doc">$html</div>'); | 
| -    } | 
| - | 
| -    // Document the top-level members. | 
| -    docMembers(library.topType); | 
| - | 
| -    // Document the types. | 
| -    final classes = <Type>[]; | 
| -    final interfaces = <Type>[]; | 
| -    final exceptions = <Type>[]; | 
| - | 
| -    for (final type in orderByName(library.types)) { | 
| -      if (type.isTop) continue; | 
| -      if (type.name.startsWith('_')) continue; | 
| - | 
| -      if (type.name.endsWith('Exception')) { | 
| -        exceptions.add(type); | 
| -      } else if (type.isClass) { | 
| -        classes.add(type); | 
| -      } else { | 
| -        interfaces.add(type); | 
| -      } | 
| -    } | 
| - | 
| -    docTypes(classes, 'Classes'); | 
| -    docTypes(interfaces, 'Interfaces'); | 
| -    docTypes(exceptions, 'Exceptions'); | 
| - | 
| -    writeFooter(); | 
| -    endFile(); | 
| - | 
| -    for (final type in library.types.getValues()) { | 
| -      if (!type.isTop) docType(type); | 
| -    } | 
| -  } | 
| - | 
| -  void docTypes(List<Type> types, String header) { | 
| -    if (types.length == 0) return; | 
| - | 
| -    writeln('<h3>$header</h3>'); | 
| - | 
| -    for (final type in types) { | 
| -      writeln( | 
| -          ''' | 
| -          <div class="type"> | 
| -          <h4> | 
| -            ${a(typeUrl(type), "<strong>${typeName(type)}</strong>")} | 
| -          </h4> | 
| -          </div> | 
| -          '''); | 
| -    } | 
| -  } | 
| - | 
| -  void docType(Type type) { | 
| -    _totalTypes++; | 
| -    _currentType = type; | 
| - | 
| -    startFile(typeUrl(type)); | 
| - | 
| -    final typeTitle = | 
| -      '${type.isClass ? "Class" : "Interface"} ${typeName(type)}'; | 
| -    writeHeader('Library ${type.library.name} / $typeTitle', | 
| -        [type.library.name, libraryUrl(type.library), | 
| -         typeName(type), typeUrl(type)]); | 
| -    writeln( | 
| -        ''' | 
| -        <h2>${type.isClass ? "Class" : "Interface"} | 
| -            <strong>${typeName(type, showBounds: true)}</strong></h2> | 
| -        '''); | 
| - | 
| -    docCode(type.span, getTypeComment(type)); | 
| -    docInheritance(type); | 
| -    docConstructors(type); | 
| -    docMembers(type); | 
| - | 
| -    writeTypeFooter(); | 
| -    writeFooter(); | 
| -    endFile(); | 
| -  } | 
| - | 
| -  /** Override this to write additional content at the end of a type's page. */ | 
| -  void writeTypeFooter() { | 
| -    // Do nothing. | 
| -  } | 
| - | 
| -  /** | 
| -   * Writes an inline type span for the given type. This is a little box with | 
| -   * an icon and the type's name. It's similar to how types appear in the | 
| -   * navigation, but is suitable for inline (as opposed to in a `<ul>`) use. | 
| -   */ | 
| -  void typeSpan(Type type) { | 
| -    var icon = 'interface'; | 
| -    if (type.name.endsWith('Exception')) { | 
| -      icon = 'exception'; | 
| -    } else if (type.isClass) { | 
| -      icon = 'class'; | 
| -    } | 
| - | 
| -    write('<span class="type-box"><span class="icon-$icon"></span>'); | 
| -    if (_currentType == type) { | 
| -      write('<strong>${typeName(type)}</strong>'); | 
| -    } else { | 
| -      write(a(typeUrl(type), typeName(type))); | 
| -    } | 
| -    write('</span>'); | 
| -  } | 
| - | 
| -  /** | 
| -   * Document the other types that touch [Type] in the inheritance hierarchy: | 
| -   * subclasses, superclasses, subinterfaces, superinferfaces, and default | 
| -   * class. | 
| -   */ | 
| -  void docInheritance(Type type) { | 
| -    // Don't show the inheritance details for Object. It doesn't have any base | 
| -    // class (obviously) and it has too many subclasses to be useful. | 
| -    if (type.isObject) return; | 
| - | 
| -    // Writes an unordered list of references to types with an optional header. | 
| -    listTypes(types, header) { | 
| -      if (types == null) return; | 
| - | 
| -      // Skip private types. | 
| -      final publicTypes = types.filter((type) => !type.name.startsWith('_')); | 
| -      if (publicTypes.length == 0) return; | 
| - | 
| -      writeln('<h3>$header</h3>'); | 
| -      writeln('<p>'); | 
| -      bool first = true; | 
| -      for (final type in publicTypes) { | 
| -        if (!first) write(', '); | 
| -        typeSpan(type); | 
| -        first = false; | 
| -      } | 
| -      writeln('</p>'); | 
| -    } | 
| - | 
| -    if (type.isClass) { | 
| -      // Show the chain of superclasses. | 
| -      if (!type.parent.isObject) { | 
| -        final supertypes = []; | 
| -        var thisType = type.parent; | 
| -        // As a sanity check, only show up to five levels of nesting, otherwise | 
| -        // the box starts to get hideous. | 
| -        do { | 
| -          supertypes.add(thisType); | 
| -          thisType = thisType.parent; | 
| -        } while (!thisType.isObject); | 
| - | 
| -        writeln('<h3>Extends</h3>'); | 
| -        writeln('<p>'); | 
| -        for (var i = supertypes.length - 1; i >= 0; i--) { | 
| -          typeSpan(supertypes[i]); | 
| -          write(' > '); | 
| -        } | 
| - | 
| -        // Write this class. | 
| -        typeSpan(type); | 
| -        writeln('</p>'); | 
| -      } | 
| - | 
| -      // Find the immediate declared subclasses (Type.subtypes includes many | 
| -      // transitive subtypes). | 
| -      final subtypes = []; | 
| -      for (final subtype in type.subtypes) { | 
| -        if (subtype.parent == type) subtypes.add(subtype); | 
| -      } | 
| -      subtypes.sort((a, b) => a.name.compareTo(b.name)); | 
| - | 
| -      listTypes(subtypes, 'Subclasses'); | 
| -      listTypes(type.interfaces, 'Implements'); | 
| -    } else { | 
| -      // Show the default class. | 
| -      if (type.genericType.defaultType != null) { | 
| -        listTypes([type.genericType.defaultType], 'Default class'); | 
| -      } | 
| - | 
| -      // List extended interfaces. | 
| -      listTypes(type.interfaces, 'Extends'); | 
| - | 
| -      // List subinterfaces and implementing classes. | 
| -      final subinterfaces = []; | 
| -      final implementing = []; | 
| - | 
| -      for (final subtype in type.subtypes) { | 
| -        // We only want explicitly declared subinterfaces, so check that this | 
| -        // type is a superinterface. | 
| -        for (final supertype in subtype.interfaces) { | 
| -          if (supertype == type) { | 
| -            if (subtype.isClass) { | 
| -              implementing.add(subtype); | 
| -            } else { | 
| -              subinterfaces.add(subtype); | 
| -            } | 
| -            break; | 
| -          } | 
| -        } | 
| -      } | 
| - | 
| -      listTypes(subinterfaces, 'Subinterfaces'); | 
| -      listTypes(implementing, 'Implemented by'); | 
| -    } | 
| -  } | 
| - | 
| -  /** Document the constructors for [Type], if any. */ | 
| -  void docConstructors(Type type) { | 
| -    final names = type.constructors.getKeys().filter( | 
| -      (name) => !name.startsWith('_')); | 
| - | 
| -    if (names.length > 0) { | 
| -      writeln('<h3>Constructors</h3>'); | 
| -      names.sort((x, y) => x.toUpperCase().compareTo(y.toUpperCase())); | 
| - | 
| -      for (final name in names) { | 
| -        docMethod(type, type.constructors[name], constructorName: name); | 
| -      } | 
| -    } | 
| -  } | 
| - | 
| -  void docMembers(Type type) { | 
| -    // Collect the different kinds of members. | 
| -    final staticMethods = []; | 
| -    final staticFields = []; | 
| -    final instanceMethods = []; | 
| -    final instanceFields = []; | 
| - | 
| -    for (final member in orderByName(type.members)) { | 
| -      if (member.name.startsWith('_')) continue; | 
| - | 
| -      final methods = member.isStatic ? staticMethods : instanceMethods; | 
| -      final fields = member.isStatic ? staticFields : instanceFields; | 
| - | 
| -      if (member.isProperty) { | 
| -        if (member.canGet) methods.add(member.getter); | 
| -        if (member.canSet) methods.add(member.setter); | 
| -      } else if (member.isMethod) { | 
| -        methods.add(member); | 
| -      } else if (member.isField) { | 
| -        fields.add(member); | 
| -      } | 
| -    } | 
| - | 
| -    if (staticMethods.length > 0) { | 
| -      final title = type.isTop ? 'Functions' : 'Static Methods'; | 
| -      writeln('<h3>$title</h3>'); | 
| -      for (final method in staticMethods) docMethod(type, method); | 
| -    } | 
| - | 
| -    if (staticFields.length > 0) { | 
| -      final title = type.isTop ? 'Variables' : 'Static Fields'; | 
| -      writeln('<h3>$title</h3>'); | 
| -      for (final field in staticFields) docField(type, field); | 
| -    } | 
| - | 
| -    if (instanceMethods.length > 0) { | 
| -      writeln('<h3>Methods</h3>'); | 
| -      for (final method in instanceMethods) docMethod(type, method); | 
| -    } | 
| - | 
| -    if (instanceFields.length > 0) { | 
| -      writeln('<h3>Fields</h3>'); | 
| -      for (final field in instanceFields) docField(type, field); | 
| -    } | 
| -  } | 
| - | 
| -  /** | 
| -   * Documents the [method] in type [type]. Handles all kinds of methods | 
| -   * including getters, setters, and constructors. | 
| -   */ | 
| -  void docMethod(Type type, MethodMember method, | 
| -      [String constructorName = null]) { | 
| -    _totalMembers++; | 
| -    _currentMember = method; | 
| - | 
| -    writeln('<div class="method"><h4 id="${memberAnchor(method)}">'); | 
| - | 
| -    if (includeSource) { | 
| -      writeln('<span class="show-code">Code</span>'); | 
| -    } | 
| - | 
| -    if (method.isConstructor) { | 
| -      write(method.isConst ? 'const ' : 'new '); | 
| -    } | 
| - | 
| -    if (constructorName == null) { | 
| -      annotateType(type, method.returnType); | 
| -    } | 
| - | 
| -    // Translate specially-named methods: getters, setters, operators. | 
| -    var name = method.name; | 
| -    if (name.startsWith('get:')) { | 
| -      // Getter. | 
| -      name = 'get ${name.substring(4)}'; | 
| -    } else if (name.startsWith('set:')) { | 
| -      // Setter. | 
| -      name = 'set ${name.substring(4)}'; | 
| -    } else if (name == ':negate') { | 
| -      // Dart uses 'negate' for prefix negate operators, not '!'. | 
| -      name = 'operator negate'; | 
| -    } else if (name == ':call') { | 
| -      name = 'operator call'; | 
| -    } else { | 
| -      // See if it's an operator. | 
| -      name = TokenKind.rawOperatorFromMethod(name); | 
| -      if (name == null) { | 
| -        name = method.name; | 
| -      } else { | 
| -        name = 'operator $name'; | 
| -      } | 
| -    } | 
| - | 
| -    write('<strong>$name</strong>'); | 
| - | 
| -    // Named constructors. | 
| -    if (constructorName != null && constructorName != '') { | 
| -      write('.'); | 
| -      write(constructorName); | 
| -    } | 
| - | 
| -    docParamList(type, method); | 
| - | 
| -    write(''' <a class="anchor-link" href="#${memberAnchor(method)}" | 
| -              title="Permalink to ${typeName(type)}.$name">#</a>'''); | 
| -    writeln('</h4>'); | 
| - | 
| -    docCode(method.span, getMethodComment(method), showCode: true); | 
| - | 
| -    writeln('</div>'); | 
| -  } | 
| - | 
| -  /** Documents the field [field] of type [type]. */ | 
| -  void docField(Type type, FieldMember field) { | 
| -    _totalMembers++; | 
| -    _currentMember = field; | 
| - | 
| -    writeln('<div class="field"><h4 id="${memberAnchor(field)}">'); | 
| - | 
| -    if (includeSource) { | 
| -      writeln('<span class="show-code">Code</span>'); | 
| -    } | 
| - | 
| -    if (field.isFinal) { | 
| -      write('final '); | 
| -    } else if (field.type.name == 'Dynamic') { | 
| -      write('var '); | 
| -    } | 
| - | 
| -    annotateType(type, field.type); | 
| -    write( | 
| -        ''' | 
| -        <strong>${field.name}</strong> <a class="anchor-link" | 
| -            href="#${memberAnchor(field)}" | 
| -            title="Permalink to ${typeName(type)}.${field.name}">#</a> | 
| -        </h4> | 
| -        '''); | 
| - | 
| -    docCode(field.span, getFieldComment(field), showCode: true); | 
| -    writeln('</div>'); | 
| -  } | 
| - | 
| -  void docParamList(Type enclosingType, MethodMember member) { | 
| -    write('('); | 
| -    bool first = true; | 
| -    bool inOptionals = false; | 
| -    for (final parameter in member.parameters) { | 
| -      if (!first) write(', '); | 
| - | 
| -      if (!inOptionals && parameter.isOptional) { | 
| -        write('['); | 
| -        inOptionals = true; | 
| -      } | 
| - | 
| -      annotateType(enclosingType, parameter.type, parameter.name); | 
| - | 
| -      // Show the default value for named optional parameters. | 
| -      if (parameter.isOptional && parameter.hasDefaultValue) { | 
| -        write(' = '); | 
| -        // TODO(rnystrom): Using the definition text here is a bit cheap. | 
| -        // We really should be pretty-printing the AST so that if you have: | 
| -        //   foo([arg = 1 + /* comment */ 2]) | 
| -        // the docs should just show: | 
| -        //   foo([arg = 1 + 2]) | 
| -        // For now, we'll assume you don't do that. | 
| -        write(parameter.definition.value.span.text); | 
| -      } | 
| - | 
| -      first = false; | 
| -    } | 
| - | 
| -    if (inOptionals) write(']'); | 
| -    write(')'); | 
| -  } | 
| - | 
| -  /** | 
| -   * Documents the code contained within [span] with [comment]. If [showCode] | 
| -   * is `true` (and [includeSource] is set), also includes the source code. | 
| -   */ | 
| -  void docCode(SourceSpan span, String comment, [bool showCode = false]) { | 
| -    writeln('<div class="doc">'); | 
| -    if (comment != null) { | 
| -      writeln(comment); | 
| -    } | 
| - | 
| -    if (includeSource && showCode) { | 
| -      writeln('<pre class="source">'); | 
| -      writeln(md.escapeHtml(unindentCode(span))); | 
| -      writeln('</pre>'); | 
| -    } | 
| - | 
| -    writeln('</div>'); | 
| -  } | 
| - | 
| -  /** Get the doc comment associated with the given type. */ | 
| -  String getTypeComment(Type type) { | 
| -    String comment = _comments.find(type.span); | 
| -    if (comment == null) return null; | 
| -    return md.markdownToHtml(comment); | 
| -  } | 
| - | 
| -  /** Get the doc comment associated with the given method. */ | 
| -  String getMethodComment(MethodMember method) { | 
| -    String comment = _comments.find(method.span); | 
| -    if (comment == null) return null; | 
| -    return md.markdownToHtml(comment); | 
| -  } | 
| - | 
| -  /** Get the doc comment associated with the given field. */ | 
| -  String getFieldComment(FieldMember field) { | 
| -    String comment = _comments.find(field.span); | 
| -    if (comment == null) return null; | 
| -    return md.markdownToHtml(comment); | 
| -  } | 
| - | 
| -  /** | 
| -   * Converts [fullPath] which is understood to be a full path from the root of | 
| -   * the generated docs to one relative to the current file. | 
| -   */ | 
| -  String relativePath(String fullPath) { | 
| -    // Don't make it relative if it's an absolute path. | 
| -    if (isAbsolute(fullPath)) return fullPath; | 
| - | 
| -    // TODO(rnystrom): Walks all the way up to root each time. Shouldn't do | 
| -    // this if the paths overlap. | 
| -    return repeat('../', countOccurrences(_filePath, '/')) + fullPath; | 
| -  } | 
| - | 
| -  /** Gets whether or not the given URL is absolute or relative. */ | 
| -  bool isAbsolute(String url) { | 
| -    // TODO(rnystrom): Why don't we have a nice type in the platform for this? | 
| -    // TODO(rnystrom): This is a bit hackish. We consider any URL that lacks | 
| -    // a scheme to be relative. | 
| -    return const RegExp(@'^\w+:').hasMatch(url); | 
| -  } | 
| - | 
| -  /** Gets the URL to the documentation for [library]. */ | 
| -  String libraryUrl(Library library) { | 
| -    return '${sanitize(library.name)}.html'; | 
| -  } | 
| - | 
| -  /** Gets the URL for the documentation for [type]. */ | 
| -  String typeUrl(Type type) { | 
| -    if (type.isTop) return '${sanitize(type.library.name)}.html'; | 
| -    // Always get the generic type to strip off any type parameters or | 
| -    // arguments. If the type isn't generic, genericType returns `this`, so it | 
| -    // works for non-generic types too. | 
| -    return '${sanitize(type.library.name)}/${type.genericType.name}.html'; | 
| -  } | 
| - | 
| -  /** Gets the URL for the documentation for [member]. */ | 
| -  String memberUrl(Member member) { | 
| -    final typeUrl = typeUrl(member.declaringType); | 
| -    if (!member.isConstructor) return '$typeUrl#${member.name}'; | 
| -    if (member.constructorName == '') return '$typeUrl#new:${member.name}'; | 
| -    return '$typeUrl#new:${member.name}.${member.constructorName}'; | 
| -  } | 
| - | 
| -  /** Gets the anchor id for the document for [member]. */ | 
| -  String memberAnchor(Member member) { | 
| -    return '${member.name}'; | 
| -  } | 
| - | 
| -  /** | 
| -   * Creates a hyperlink. Handles turning the [href] into an appropriate | 
| -   * relative path from the current file. | 
| -   */ | 
| -  String a(String href, String contents, [String css]) { | 
| -    // Mark outgoing external links, mainly so we can style them. | 
| -    final rel = isAbsolute(href) ? ' ref="external"' : ''; | 
| -    final cssClass = css == null ? '' : ' class="$css"'; | 
| -    return '<a href="${relativePath(href)}"$cssClass$rel>$contents</a>'; | 
| -  } | 
| - | 
| -  /** | 
| -   * Writes a type annotation for the given type and (optional) parameter name. | 
| -   */ | 
| -  annotateType(Type enclosingType, Type type, [String paramName = null]) { | 
| -    // Don't bother explicitly displaying Dynamic. | 
| -    if (type.isVar) { | 
| -      if (paramName !== null) write(paramName); | 
| -      return; | 
| -    } | 
| - | 
| -    // For parameters, handle non-typedefed function types. | 
| -    if (paramName !== null) { | 
| -      final call = type.getCallMethod(); | 
| -      if (call != null) { | 
| -        annotateType(enclosingType, call.returnType); | 
| -        write(paramName); | 
| - | 
| -        docParamList(enclosingType, call); | 
| -        return; | 
| -      } | 
| -    } | 
| - | 
| -    linkToType(enclosingType, type); | 
| - | 
| -    write(' '); | 
| -    if (paramName !== null) write(paramName); | 
| -  } | 
| - | 
| -  /** Writes a link to a human-friendly string representation for a type. */ | 
| -  linkToType(Type enclosingType, Type type) { | 
| -    if (type is ParameterType) { | 
| -      // If we're using a type parameter within the body of a generic class then | 
| -      // just link back up to the class. | 
| -      write(a(typeUrl(enclosingType), type.name)); | 
| -      return; | 
| -    } | 
| - | 
| -    // Link to the type. | 
| -    // Use .genericType to avoid writing the <...> here. | 
| -    write(a(typeUrl(type), type.genericType.name)); | 
| - | 
| -    // See if it's a generic type. | 
| -    if (type.isGeneric) { | 
| -      // TODO(rnystrom): This relies on a weird corner case of frog. Currently, | 
| -      // the only time we get into this case is when we have a "raw" generic | 
| -      // that's been instantiated with Dynamic for all type arguments. It's kind | 
| -      // of strange that frog works that way, but we take advantage of it to | 
| -      // show raw types without any type arguments. | 
| -      return; | 
| -    } | 
| - | 
| -    // See if it's an instantiation of a generic type. | 
| -    final typeArgs = type.typeArgsInOrder; | 
| -    if (typeArgs.length > 0) { | 
| -      write('<'); | 
| -      bool first = true; | 
| -      for (final arg in typeArgs) { | 
| -        if (!first) write(', '); | 
| -        first = false; | 
| -        linkToType(enclosingType, arg); | 
| -      } | 
| -      write('>'); | 
| -    } | 
| -  } | 
| - | 
| -  /** Creates a linked cross reference to [type]. */ | 
| -  typeReference(Type type) { | 
| -    // TODO(rnystrom): Do we need to handle ParameterTypes here like | 
| -    // annotation() does? | 
| -    return a(typeUrl(type), typeName(type), css: 'crossref'); | 
| -  } | 
| - | 
| -  /** Generates a human-friendly string representation for a type. */ | 
| -  typeName(Type type, [bool showBounds = false]) { | 
| -    // See if it's a generic type. | 
| -    if (type.isGeneric) { | 
| -      final typeParams = []; | 
| -      for (final typeParam in type.genericType.typeParameters) { | 
| -        if (showBounds && | 
| -            (typeParam.extendsType != null) && | 
| -            !typeParam.extendsType.isObject) { | 
| -          final bound = typeName(typeParam.extendsType, showBounds: true); | 
| -          typeParams.add('${typeParam.name} extends $bound'); | 
| -        } else { | 
| -          typeParams.add(typeParam.name); | 
| -        } | 
| -      } | 
| - | 
| -      final params = Strings.join(typeParams, ', '); | 
| -      return '${type.name}<$params>'; | 
| -    } | 
| - | 
| -    // See if it's an instantiation of a generic type. | 
| -    final typeArgs = type.typeArgsInOrder; | 
| -    if (typeArgs.length > 0) { | 
| -      final args = Strings.join(map(typeArgs, (arg) => typeName(arg)), ', '); | 
| -      return '${type.genericType.name}<$args>'; | 
| -    } | 
| - | 
| -    // Regular type. | 
| -    return type.name; | 
| -  } | 
| - | 
| -  /** | 
| -   * Remove leading indentation to line up with first line. | 
| -   */ | 
| -  unindentCode(SourceSpan span) { | 
| -    final column = getSpanColumn(span); | 
| -    final lines = span.text.split('\n'); | 
| -    // TODO(rnystrom): Dirty hack. | 
| -    for (var i = 1; i < lines.length; i++) { | 
| -      lines[i] = unindent(lines[i], column); | 
| -    } | 
| - | 
| -    final code = Strings.join(lines, '\n'); | 
| -    return code; | 
| -  } | 
| - | 
| -  /** | 
| -   * Takes a string of Dart code and turns it into sanitized HTML. | 
| -   */ | 
| -  formatCode(SourceSpan span) { | 
| -    final code = unindentCode(span); | 
| - | 
| -    // Syntax highlight. | 
| -    return classifySource(new SourceFile('', code)); | 
| -  } | 
| - | 
| -  /** | 
| -   * This will be called whenever a doc comment hits a `[name]` in square | 
| -   * brackets. It will try to figure out what the name refers to and link or | 
| -   * style it appropriately. | 
| -   */ | 
| -  md.Node resolveNameReference(String name, [Member member = null, | 
| -      Type type = null, Library library = null]) { | 
| -    makeLink(String href) { | 
| -      final anchor = new md.Element.text('a', name); | 
| -      anchor.attributes['href'] = relativePath(href); | 
| -      anchor.attributes['class'] = 'crossref'; | 
| -      return anchor; | 
| -    } | 
| - | 
| -    findMember(Type type, String memberName) { | 
| -      final member = type.members[memberName]; | 
| -      if (member == null) return null; | 
| - | 
| -      // Special case: if the member we've resolved is a property (i.e. it wraps | 
| -      // a getter and/or setter then *that* member itself won't be on the docs, | 
| -      // just the getter or setter will be. So pick one of those to link to. | 
| -      if (member.isProperty) { | 
| -        return member.canGet ? member.getter : member.setter; | 
| -      } | 
| - | 
| -      return member; | 
| -    } | 
| - | 
| -    // See if it's a parameter of the current method. | 
| -    if (member != null) { | 
| -      for (final parameter in member.parameters) { | 
| -        if (parameter.name == name) { | 
| -          final element = new md.Element.text('span', name); | 
| -          element.attributes['class'] = 'param'; | 
| -          return element; | 
| -        } | 
| -      } | 
| -    } | 
| - | 
| -    // See if it's another member of the current type. | 
| -    if (type != null) { | 
| -      final member = findMember(type, name); | 
| -      if (member != null) { | 
| -        return makeLink(memberUrl(member)); | 
| -      } | 
| -    } | 
| - | 
| -    // See if it's another type or a member of another type in the current | 
| -    // library. | 
| -    if (library != null) { | 
| -      // See if it's a constructor | 
| -      final constructorLink = (() { | 
| -        final match = new RegExp(@'new (\w+)(?:\.(\w+))?').firstMatch(name); | 
| -        if (match == null) return; | 
| -        final type = library.types[match[1]]; | 
| -        if (type == null) return; | 
| -        final constructor = type.getConstructor( | 
| -            match[2] == null ? '' : match[2]); | 
| -        if (constructor == null) return; | 
| -        return makeLink(memberUrl(constructor)); | 
| -      })(); | 
| -      if (constructorLink != null) return constructorLink; | 
| - | 
| -      // See if it's a member of another type | 
| -      final foreignMemberLink = (() { | 
| -        final match = new RegExp(@'(\w+)\.(\w+)').firstMatch(name); | 
| -        if (match == null) return; | 
| -        final type = library.types[match[1]]; | 
| -        if (type == null) return; | 
| -        final member = findMember(type, match[2]); | 
| -        if (member == null) return; | 
| -        return makeLink(memberUrl(member)); | 
| -      })(); | 
| -      if (foreignMemberLink != null) return foreignMemberLink; | 
| - | 
| -      final type = library.types[name]; | 
| -      if (type != null) { | 
| -        return makeLink(typeUrl(type)); | 
| -      } | 
| - | 
| -      // See if it's a top-level member in the current library. | 
| -      final member = findMember(library.topType, name); | 
| -      if (member != null) { | 
| -        return makeLink(memberUrl(member)); | 
| -      } | 
| -    } | 
| - | 
| -    // TODO(rnystrom): Should also consider: | 
| -    // * Names imported by libraries this library imports. | 
| -    // * Type parameters of the enclosing type. | 
| - | 
| -    return new md.Element.text('code', name); | 
| -  } | 
| - | 
| -  // TODO(rnystrom): Move into SourceSpan? | 
| -  int getSpanColumn(SourceSpan span) { | 
| -    final line = span.file.getLine(span.start); | 
| -    return span.file.getColumn(line, span.start); | 
| -  } | 
| -} | 
|  |