Chromium Code Reviews| Index: utils/dartdoc/dartdoc.dart |
| diff --git a/utils/dartdoc/dartdoc.dart b/utils/dartdoc/dartdoc.dart |
| index a77fb1ae3898baa7474a53c521dbb1afb548b8f5..56ed834abbc7e83829801a7b763a542fe07519f4 100644 |
| --- a/utils/dartdoc/dartdoc.dart |
| +++ b/utils/dartdoc/dartdoc.dart |
| @@ -21,6 +21,7 @@ |
| #import('../markdown/lib.dart', prefix: 'md'); |
| #source('classify.dart'); |
| +#source('comment_map.dart'); |
| #source('files.dart'); |
| #source('utils.dart'); |
| @@ -30,63 +31,19 @@ final corePath = 'lib'; |
| /** Path to generate html files into. */ |
| final outdir = 'docs'; |
| -/** Set to `false` to not include the source code in the generated docs. */ |
| -bool includeSource = true; |
| - |
| FileSystem files; |
| -/** Special comment position used to store the library-level doc comment. */ |
| -final _libraryDoc = -1; |
| - |
| -/** 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 cached lookup-table to associate doc comments with spans. The outer map |
| - * is from filenames to doc comments in that file. The inner map maps from the |
| - * token positions to doc comments. Each position is the starting offset of the |
| - * next non-comment token *following* the doc comment. For example, the position |
| - * for this comment would be the position of the "Map" token below. |
| - */ |
| -Map<String, Map<int, String>> _comments; |
| - |
| -/** A callback that returns additional Markdown documentation for a type. */ |
| -typedef String TypeDocumenter(Type type); |
| - |
| -/** A list of callbacks registered for documenting types. */ |
| -List<TypeDocumenter> _typeDocumenters; |
| - |
| -/** A callback that returns additional Markdown documentation for a method. */ |
| -typedef String MethodDocumenter(MethodMember method); |
| - |
| -/** A list of callbacks registered for documenting methods. */ |
| -List<MethodDocumenter> _methodDocumenters; |
| - |
| -/** A callback that returns additional Markdown documentation for a field. */ |
| -typedef String FieldDocumenter(FieldMember field); |
| - |
| -/** A list of callbacks registered for documenting fields. */ |
| -List<FieldDocumenter> _fieldDocumenters; |
| - |
| -int _totalLibraries = 0; |
| -int _totalTypes = 0; |
| -int _totalMembers = 0; |
| - |
| /** |
| * Run this from the `utils/dartdoc` directory. |
| */ |
| void main() { |
| // The entrypoint of the library to generate docs for. |
| - final entrypoint = process.argv[2]; |
| + final entrypoint = process.argv[process.argv.length - 1]; |
| // Parse the dartdoc options. |
| - for (int i = 3; i < process.argv.length; i++) { |
| + bool includeSource = true; |
| + |
| + for (int i = 2; i < process.argv.length - 1; i++) { |
| final arg = process.argv[i]; |
| switch (arg) { |
| case '--no-code': |
| @@ -102,811 +59,739 @@ void main() { |
| parseOptions('../../frog', [] /* args */, files); |
| initializeWorld(files); |
| + var dartdoc; |
| final elapsed = time(() { |
| - initializeDartDoc(); |
| - document(entrypoint); |
| + dartdoc = new Dartdoc(); |
| + dartdoc.includeSource = includeSource; |
| + dartdoc.document(entrypoint); |
| }); |
| - printStats(elapsed); |
| -} |
| - |
| -void initializeDartDoc() { |
| - _comments = <Map<int, String>>{}; |
| - _typeDocumenters = <TypeDocumenter>[]; |
| - _methodDocumenters = <MethodDocumenter>[]; |
| - _fieldDocumenters = <FieldDocumenter>[]; |
| - |
| - // 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(resolveNameReference); |
| + print('Documented ${dartdoc._totalLibraries} libraries, ' + |
| + '${dartdoc._totalTypes} types, and ' + |
| + '${dartdoc._totalMembers} members in ${elapsed}msec.'); |
| } |
| -document(String entrypoint) { |
| - try { |
| - var oldDietParse = options.dietParse; |
| - 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.process(); |
| - break; |
| - |
| - case 'dom': |
| - world.getOrAddLibrary('dart:core'); |
| - world.getOrAddLibrary('dart:coreimpl'); |
| - world.getOrAddLibrary('dart:json'); |
| - world.getOrAddLibrary('dart:dom'); |
| - world.process(); |
| - break; |
| +class Dartdoc { |
| + /** Set to `false` to not include the source code in the generated docs. */ |
| + bool includeSource = true; |
| + |
| + /** |
| + * The cached lookup-table to associate doc comments with spans. The outer map |
| + * is from filenames to doc comments in that file. The inner map maps from the |
| + * token positions to doc comments. Each position is the starting offset of |
| + * the next non-comment token *following* the doc comment. For example, the |
| + * position for this comment would be the position of the "Map" token below. |
| + */ |
| + CommentMap _comments; |
|
nweiz
2011/12/15 19:39:04
This long doc comment seems redundant with the Com
Bob Nystrom
2011/12/15 20:20:11
Done.
|
| + |
| + /** 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; |
| + |
| + int _totalLibraries = 0; |
| + int _totalTypes = 0; |
| + int _totalMembers = 0; |
| + |
| + Dartdoc() |
| + : _comments = new CommentMap() { |
|
nweiz
2011/12/15 19:39:04
I thought initializer lists were supposed to be on
Bob Nystrom
2011/12/15 20:20:11
I don't think we specify one way or the other. I t
|
| + // 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)*?)\:\]')); |
|
nweiz
2011/12/15 19:39:04
Won't this add a new syntax entry to the markdown
Bob Nystrom
2011/12/15 20:20:11
Hmm, yes. It shouldn't cause any problems but it i
|
| + |
| + md.setImplicitLinkResolver(resolveNameReference); |
|
nweiz
2011/12/15 19:39:04
This also seems potentially troublesome if someone
Bob Nystrom
2011/12/15 20:20:11
Yeah... :(
|
| + } |
| + |
| + document(String entrypoint) { |
| + try { |
| + var oldDietParse = options.dietParse; |
| + 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.process(); |
| + break; |
| + |
| + case 'dom': |
| + world.getOrAddLibrary('dart:core'); |
| + world.getOrAddLibrary('dart:coreimpl'); |
| + world.getOrAddLibrary('dart:json'); |
| + world.getOrAddLibrary('dart:dom'); |
| + 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.process(); |
| + break; |
| + |
| + default: |
| + // Normal entrypoint script. |
| + world.processDartScript(entrypoint); |
| + } |
| - case 'html': |
| - world.getOrAddLibrary('dart:core'); |
| - world.getOrAddLibrary('dart:coreimpl'); |
| - world.getOrAddLibrary('dart:json'); |
| - world.getOrAddLibrary('dart:dom'); |
| - world.getOrAddLibrary('dart:html'); |
| - world.process(); |
| - break; |
| + world.resolveAll(); |
| - default: |
| - // Normal entrypoint script. |
| - world.processDartScript(entrypoint); |
| + // Generate the docs. |
| + docIndex(); |
| + for (final library in world.libraries.getValues()) { |
| + docLibrary(library); |
| + } |
| + } finally { |
| + options.dietParse = oldDietParse; |
| } |
| + } |
| - world.resolveAll(); |
| - |
| - // Generate the docs. |
| - docIndex(); |
| - for (final library in world.libraries.getValues()) { |
| - docLibrary(library); |
| - } |
| - } finally { |
| - options.dietParse = oldDietParse; |
| + writeHeader(String title) { |
| + writeln( |
| + ''' |
| + <!DOCTYPE html> |
| + <html> |
| + <head> |
| + <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"> |
| + <script src="${relativePath('interact.js')}"></script> |
| + </head> |
| + <body> |
| + <div class="page"> |
| + '''); |
| + docNavigation(); |
| + writeln('<div class="content">'); |
| } |
| -} |
| -printStats(num elapsed) { |
| - print('Documented $_totalLibraries libraries, $_totalTypes types, and ' + |
| - '$_totalMembers members in ${elapsed}msec.'); |
| -} |
| + writeFooter() { |
| + writeln( |
| + ''' |
| + </div> |
| + <div class="footer"</div> |
| + </body></html> |
| + '''); |
| + } |
| -writeHeader(String title) { |
| - writeln( |
| - ''' |
| - <!DOCTYPE html> |
| - <html> |
| - <head> |
| - <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"> |
| - <script src="${relativePath('interact.js')}"></script> |
| - </head> |
| - <body> |
| - <div class="page"> |
| - '''); |
| - docNavigation(); |
| - writeln('<div class="content">'); |
| -} |
| + docIndex() { |
| + startFile('index.html'); |
| -writeFooter() { |
| - writeln( |
| - ''' |
| - </div> |
| - <div class="footer"</div> |
| - </body></html> |
| - '''); |
| -} |
| + writeHeader('Dart Documentation'); |
| -docIndex() { |
| - startFile('index.html'); |
| + writeln('<h1>Dart Documentation</h1>'); |
| + writeln('<h3>Libraries</h3>'); |
| - writeHeader('Dart Documentation'); |
| + for (final library in orderByName(world.libraries)) { |
| + writeln( |
| + ''' |
| + <h4>${a(libraryUrl(library), library.name)}</h4> |
| + '''); |
| + } |
| - writeln('<h1>Dart Documentation</h1>'); |
| - writeln('<h3>Libraries</h3>'); |
| + writeFooter(); |
| + endFile(); |
| + } |
| - for (final library in orderByName(world.libraries)) { |
| + docNavigation() { |
| writeln( |
| ''' |
| - <h4>${a(libraryUrl(library), library.name)}</h4> |
| + <div class="nav"> |
| + <h1>${a("index.html", "Dart Documentation")}</h1> |
| '''); |
| - } |
| - writeFooter(); |
| - endFile(); |
| -} |
| - |
| -docNavigation() { |
| - writeln( |
| - ''' |
| - <div class="nav"> |
| - <h1>${a("index.html", "Dart Documentation")}</h1> |
| - '''); |
| + for (final library in orderByName(world.libraries)) { |
| + write('<h2><div class="icon-library"></div>'); |
| - for (final library in orderByName(world.libraries)) { |
| - 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>'); |
| - if ((_currentLibrary == library) && (_currentType == null)) { |
| - write('<strong>${library.name}</strong>'); |
| - } else { |
| - write('${a(libraryUrl(library), library.name)}'); |
| + // Only expand classes in navigation for current library. |
| + if (_currentLibrary == library) docLibraryNavigation(library); |
| } |
| - write('</h2>'); |
| - // Only expand classes in navigation for current library. |
| - if (_currentLibrary == library) docLibraryNavigation(library); |
| + writeln('</div>'); |
| } |
| - writeln('</div>'); |
| -} |
| - |
| -/** Writes the navigation for the types contained by the given library. */ |
| -docLibraryNavigation(Library library) { |
| - // Show the exception types separately. |
| - final types = <Type>[]; |
| - final exceptions = <Type>[]; |
| + /** Writes the navigation for the types contained by the given library. */ |
| + 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; |
| + 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 (type.name.endsWith('Exception')) { |
| + exceptions.add(type); |
| + } else { |
| + types.add(type); |
| + } |
| } |
| - } |
| - if ((types.length == 0) && (exceptions.length == 0)) return; |
| + if ((types.length == 0) && (exceptions.length == 0)) return; |
| - writeType(String icon, Type type) { |
| - 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)}')); |
| + writeType(String icon, Type type) { |
| + 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>'); |
| } |
| - writeln('</li>'); |
| + |
| + writeln('<ul>'); |
| + types.forEach((type) => writeType(type.isClass ? 'class' : 'interface', |
| + type)); |
| + exceptions.forEach((type) => writeType('exception', type)); |
| + writeln('</ul>'); |
| } |
| - writeln('<ul>'); |
| - types.forEach((type) => writeType(type.isClass ? 'class' : 'interface', |
| - type)); |
| - exceptions.forEach((type) => writeType('exception', type)); |
| - writeln('</ul>'); |
| -} |
| + docLibrary(Library library) { |
| + _totalLibraries++; |
| + _currentLibrary = library; |
| + _currentType = null; |
| -String _runDocumenters(var item, List<Function> documenters) => |
| - Strings.join(map(documenters, (doc) => doc(item)), '\n\n'); |
| + startFile(libraryUrl(library)); |
| + writeHeader(library.name); |
| + writeln('<h1>Library <strong>${library.name}</strong></h1>'); |
| -docLibrary(Library library) { |
| - _totalLibraries++; |
| - _currentLibrary = library; |
| - _currentType = null; |
| + // 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>'); |
| + } |
| - startFile(libraryUrl(library)); |
| - writeHeader(library.name); |
| - writeln('<h1>Library <strong>${library.name}</strong></h1>'); |
| + // Document the top-level members. |
| + docMembers(library.topType); |
| - // Look for a comment for the entire library. |
| - final comment = findCommentInFile(library.baseSource, _libraryDoc); |
| - if (comment != null) { |
| - final html = md.markdownToHtml(comment); |
| - writeln('<div class="doc">$html</div>'); |
| - } |
| + // 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; |
| - // Document the top-level members. |
| - docMembers(library.topType); |
| + if (type.name.endsWith('Exception')) { |
| + exceptions.add(type); |
| + } else if (type.isClass) { |
| + classes.add(type); |
| + } else { |
| + interfaces.add(type); |
| + } |
| + } |
| - // Document the types. |
| - final classes = <Type>[]; |
| - final interfaces = <Type>[]; |
| - final exceptions = <Type>[]; |
| + docTypes(classes, 'Classes'); |
| + docTypes(interfaces, 'Interfaces'); |
| + docTypes(exceptions, 'Exceptions'); |
| - for (final type in orderByName(library.types)) { |
| - if (type.isTop) continue; |
| - if (type.name.startsWith('_')) continue; |
| + writeFooter(); |
| + endFile(); |
| - if (type.name.endsWith('Exception')) { |
| - exceptions.add(type); |
| - } else if (type.isClass) { |
| - classes.add(type); |
| - } else { |
| - interfaces.add(type); |
| + for (final type in library.types.getValues()) { |
| + if (!type.isTop) docType(type); |
| } |
| } |
| - docTypes(classes, 'Classes'); |
| - docTypes(interfaces, 'Interfaces'); |
| - docTypes(exceptions, 'Exceptions'); |
| + docTypes(List<Type> types, String header) { |
| + if (types.length == 0) return; |
| - writeFooter(); |
| - endFile(); |
| + writeln('<h3>$header</h3>'); |
| - for (final type in library.types.getValues()) { |
| - if (!type.isTop) docType(type); |
| + for (final type in types) { |
| + writeln( |
| + ''' |
| + <div class="type"> |
| + <h4> |
| + ${a(typeUrl(type), "<strong>${typeName(type)}</strong>")} |
| + </h4> |
| + </div> |
| + '''); |
| + } |
| } |
| -} |
| -docTypes(List<Type> types, String header) { |
| - if (types.length == 0) return; |
| + docType(Type type) { |
| + _totalTypes++; |
| + _currentType = type; |
| - writeln('<h3>$header</h3>'); |
| + startFile(typeUrl(type)); |
| - for (final type in types) { |
| + final typeTitle = |
| + '${type.isClass ? "Class" : "Interface"} ${typeName(type)}'; |
| + writeHeader('Library ${type.library.name} / $typeTitle'); |
| writeln( |
| ''' |
| - <div class="type"> |
| - <h4> |
| - ${a(typeUrl(type), "<strong>${typeName(type)}</strong>")} |
| - </h4> |
| - </div> |
| + <h1>${a(libraryUrl(type.library), |
| + "Library <strong>${type.library.name}</strong>")}</h1> |
| + <h2>${type.isClass ? "Class" : "Interface"} |
| + <strong>${typeName(type, showBounds: true)}</strong></h2> |
| '''); |
| - } |
| -} |
| -docType(Type type) { |
| - _totalTypes++; |
| - _currentType = type; |
| - |
| - startFile(typeUrl(type)); |
| - |
| - final typeTitle = '${type.isClass ? "Class" : "Interface"} ${typeName(type)}'; |
| - writeHeader('Library ${type.library.name} / $typeTitle'); |
| - writeln( |
| - ''' |
| - <h1>${a(libraryUrl(type.library), |
| - "Library <strong>${type.library.name}</strong>")}</h1> |
| - <h2>${type.isClass ? "Class" : "Interface"} |
| - <strong>${typeName(type, showBounds: true)}</strong></h2> |
| - '''); |
| - |
| - docInheritance(type); |
| - docCode(type.span, _runDocumenters(type, _typeDocumenters)); |
| - docConstructors(type); |
| - docMembers(type); |
| - |
| - writeFooter(); |
| - endFile(); |
| -} |
| - |
| -void docMembers(Type type) { |
| - // Collect the different kinds of members. |
| - final methods = []; |
| - final fields = []; |
| + docInheritance(type); |
| - for (final member in orderByName(type.members)) { |
| - if (member.name.startsWith('_')) continue; |
| + docCode(type.span, getTypeComment(type)); |
| + docConstructors(type); |
| + docMembers(type); |
| - 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); |
| - } |
| + writeFooter(); |
| + endFile(); |
| } |
| - if (methods.length > 0) { |
| - writeln('<h3>Methods</h3>'); |
| - for (final method in methods) docMethod(type, method); |
| - } |
| - |
| - if (fields.length > 0) { |
| - writeln('<h3>Fields</h3>'); |
| - for (final field in fields) docField(type, field); |
| - } |
| -} |
| + /** Document the superclass, superinterfaces and factory of [Type]. */ |
| + docInheritance(Type type) { |
| + final isSubclass = (type.parent != null) && !type.parent.isObject; |
| -/** Document the superclass, superinterfaces and factory of [Type]. */ |
| -docInheritance(Type type) { |
| - final isSubclass = (type.parent != null) && !type.parent.isObject; |
| - |
| - Type factory; |
| - if (type.definition is TypeDefinition) { |
| - TypeDefinition definition = type.definition; |
| - if (definition.factoryType != null) { |
| - factory = definition.factoryType.type; |
| + Type factory; |
| + if (type.definition is TypeDefinition) { |
| + TypeDefinition definition = type.definition; |
| + if (definition.factoryType != null) { |
| + factory = definition.factoryType.type; |
| + } |
| } |
| - } |
| - if (isSubclass || |
| - (type.interfaces != null && type.interfaces.length > 0) || |
| - (factory != null)) { |
| - writeln('<p>'); |
| + if (isSubclass || |
| + (type.interfaces != null && type.interfaces.length > 0) || |
| + (factory != null)) { |
| + writeln('<p>'); |
| - if (isSubclass) { |
| - write('Extends ${typeReference(type.parent)}. '); |
| - } |
| + if (isSubclass) { |
| + write('Extends ${typeReference(type.parent)}. '); |
| + } |
| - if (type.interfaces != null && type.interfaces.length > 0) { |
| - var interfaceStr = joinWithCommas(map(type.interfaces, typeReference)); |
| - write('Implements ${interfaceStr}. '); |
| - } |
| + if (type.interfaces != null && type.interfaces.length > 0) { |
| + var interfaceStr = joinWithCommas(map(type.interfaces, typeReference)); |
| + write('Implements ${interfaceStr}. '); |
| + } |
| - if (factory != null) { |
| - write('Has factory class ${typeReference(factory)}.'); |
| + if (factory != null) { |
| + write('Has factory class ${typeReference(factory)}.'); |
| + } |
| } |
| } |
| -} |
| -/** Document the constructors for [Type], if any. */ |
| -docConstructors(Type type) { |
| - final names = type.constructors.getKeys().filter( |
| - (name) => !name.startsWith('_')); |
| + /** Document the constructors for [Type], if any. */ |
| + 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())); |
| + 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); |
| + for (final name in names) { |
| + docMethod(type, type.constructors[name], constructorName: name); |
| + } |
| } |
| } |
| -} |
| - |
| -/** |
| - * Documents the [method] in type [type]. Handles all kinds of methods |
| - * including getters, setters, and constructors. |
| - */ |
| -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>'); |
| - } |
| + void docMembers(Type type) { |
| + // Collect the different kinds of members. |
| + final methods = []; |
| + final fields = []; |
| - if (method.isStatic && !type.isTop) { |
| - write('static '); |
| - } |
| + for (final member in orderByName(type.members)) { |
| + if (member.name.startsWith('_')) continue; |
| - if (method.isConstructor) { |
| - write(method.isConst ? 'const ' : 'new '); |
| - } |
| + 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 (constructorName == null) { |
| - annotateType(type, method.returnType); |
| - } |
| + if (methods.length > 0) { |
| + writeln('<h3>Methods</h3>'); |
| + for (final method in methods) docMethod(type, method); |
| + } |
| - // 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 { |
| - // See if it's an operator. |
| - name = TokenKind.rawOperatorFromMethod(name); |
| - if (name == null) { |
| - name = method.name; |
| - } else { |
| - name = 'operator $name'; |
| + if (fields.length > 0) { |
| + writeln('<h3>Fields</h3>'); |
| + for (final field in fields) docField(type, field); |
| } |
| } |
| - write('<strong>$name</strong>'); |
| - |
| - // Named constructors. |
| - if (constructorName != null && constructorName != '') { |
| - write('.'); |
| - write(constructorName); |
| - } |
| + /** |
| + * Documents the [method] in type [type]. Handles all kinds of methods |
| + * including getters, setters, and constructors. |
| + */ |
| + docMethod(Type type, MethodMember method, [String constructorName = null]) { |
| + _totalMembers++; |
| + _currentMember = method; |
| - docParamList(type, method); |
| + writeln('<div class="method"><h4 id="${memberAnchor(method)}">'); |
| - write(''' <a class="anchor-link" href="#${memberAnchor(method)}" |
| - title="Permalink to ${typeName(type)}.$name">#</a>'''); |
| - writeln('</h4>'); |
| + if (includeSource) { |
| + writeln('<span class="show-code">Code</span>'); |
| + } |
| - docCode(method.span, _runDocumenters(method, _methodDocumenters), |
| - showCode: true); |
| + if (method.isStatic && !type.isTop) { |
| + write('static '); |
| + } |
| - writeln('</div>'); |
| -} |
| + if (method.isConstructor) { |
| + write(method.isConst ? 'const ' : 'new '); |
| + } |
| -docParamList(Type enclosingType, MethodMember member) { |
| - write('('); |
| - bool first = true; |
| - bool inOptionals = false; |
| - for (final parameter in member.parameters) { |
| - if (!first) write(', '); |
| + if (constructorName == null) { |
| + annotateType(type, method.returnType); |
| + } |
| - if (!inOptionals && parameter.isOptional) { |
| - write('['); |
| - inOptionals = true; |
| + // 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 { |
| + // See if it's an operator. |
| + name = TokenKind.rawOperatorFromMethod(name); |
| + if (name == null) { |
| + name = method.name; |
| + } else { |
| + name = 'operator $name'; |
| + } |
| } |
| - annotateType(enclosingType, parameter.type, parameter.name); |
| + write('<strong>$name</strong>'); |
| - // 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); |
| + // Named constructors. |
| + if (constructorName != null && constructorName != '') { |
| + write('.'); |
| + write(constructorName); |
| } |
| - first = false; |
| - } |
| - |
| - if (inOptionals) write(']'); |
| - write(')'); |
| -} |
| + docParamList(type, method); |
| -/** Documents the field [field] of type [type]. */ |
| -docField(Type type, FieldMember field) { |
| - _totalMembers++; |
| - _currentMember = field; |
| + write(''' <a class="anchor-link" href="#${memberAnchor(method)}" |
| + title="Permalink to ${typeName(type)}.$name">#</a>'''); |
| + writeln('</h4>'); |
| - writeln('<div class="field"><h4 id="${memberAnchor(field)}">'); |
| + docCode(method.span, getMethodComment(method), showCode: true); |
| - if (includeSource) { |
| - writeln('<span class="show-code">Code</span>'); |
| + writeln('</div>'); |
| } |
| - if (field.isStatic && !type.isTop) { |
| - write('static '); |
| - } |
| + /** Documents the field [field] of type [type]. */ |
| + docField(Type type, FieldMember field) { |
| + _totalMembers++; |
| + _currentMember = field; |
| - if (field.isFinal) { |
| - write('final '); |
| - } else if (field.type.name == 'Dynamic') { |
| - write('var '); |
| - } |
| + writeln('<div class="field"><h4 id="${memberAnchor(field)}">'); |
| - 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, _runDocumenters(field, _fieldDocumenters), |
| - showCode: true); |
| - writeln('</div>'); |
| -} |
| + if (includeSource) { |
| + writeln('<span class="show-code">Code</span>'); |
| + } |
| -/** |
| - * Creates a hyperlink. Handles turning the [href] into an appropriate relative |
| - * path from the current file. |
| - */ |
| -String a(String href, String contents, [String class]) { |
| - final css = class == null ? '' : ' class="$class"'; |
| - return '<a href="${relativePath(href)}"$css>$contents</a>'; |
| -} |
| + if (field.isStatic && !type.isTop) { |
| + write('static '); |
| + } |
| -/** 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); |
| - } |
| + if (field.isFinal) { |
| + write('final '); |
| + } else if (field.type.name == 'Dynamic') { |
| + write('var '); |
| } |
| - final params = Strings.join(typeParams, ', '); |
| - return '${type.name}<$params>'; |
| - } |
| + 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> |
| + '''); |
| - // See if it's an instantiation of a generic type. |
| - final typeArgs = type.typeArgsInOrder; |
| - if (typeArgs != null) { |
| - final args = Strings.join(map(typeArgs, typeName), ', '); |
| - return '${type.genericType.name}<$args>'; |
| + docCode(field.span, getFieldComment(field), showCode: true); |
| + writeln('</div>'); |
| } |
| - // Regular type. |
| - return type.name; |
| -} |
| + docParamList(Type enclosingType, MethodMember member) { |
| + write('('); |
| + bool first = true; |
| + bool inOptionals = false; |
| + for (final parameter in member.parameters) { |
| + if (!first) write(', '); |
| -/** 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; |
| - } |
| + if (!inOptionals && parameter.isOptional) { |
| + write('['); |
| + inOptionals = true; |
| + } |
| - // 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; |
| - } |
| + 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); |
| + } |
| - // See if it's an instantiation of a generic type. |
| - final typeArgs = type.typeArgsInOrder; |
| - if (typeArgs != null) { |
| - 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), class: 'crossref'); |
| -} |
| -/** |
| - * 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; |
| + if (inOptionals) write(']'); |
| + write(')'); |
| } |
| - // For parameters, handle non-typedefed function types. |
| - if (paramName !== null) { |
| - final call = type.getCallMethod(); |
| - if (call != null) { |
| - annotateType(enclosingType, call.returnType); |
| - write(paramName); |
| + /** |
| + * Documents the code contained within [span] with [comment]. If [showCode] |
| + * is `true` (and [includeSource] is set), also includes the source code. |
| + */ |
| + docCode(SourceSpan span, String comment, [bool showCode = false]) { |
| + writeln('<div class="doc">'); |
| + if (comment != null) { |
| + writeln(md.markdownToHtml(comment)); |
| + } |
| - docParamList(enclosingType, call); |
| - return; |
| + if (includeSource && showCode) { |
| + writeln('<pre class="source">'); |
| + write(formatCode(span)); |
| + writeln('</pre>'); |
| } |
| + |
| + writeln('</div>'); |
| } |
| - linkToType(enclosingType, type); |
| + /** Get the doc comment associated with the given type. */ |
| + String getTypeComment(Type type) => _comments.find(type.span); |
| - write(' '); |
| - if (paramName !== null) write(paramName); |
| -} |
| + /** Get the doc comment associated with the given method. */ |
| + String getMethodComment(MethodMember method) => _comments.find(method.span); |
| + /** Get the doc comment associated with the given field. */ |
| + String getFieldComment(FieldMember field) => _comments.find(field.span); |
| -/** |
| - * 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) { |
| - makeLink(String href) { |
| - final anchor = new md.Element.text('a', name); |
| - anchor.attributes['href'] = relativePath(href); |
| - anchor.attributes['class'] = 'crossref'; |
| - return anchor; |
| + /** |
| + * Creates a hyperlink. Handles turning the [href] into an appropriate |
| + * relative path from the current file. |
| + */ |
| + String a(String href, String contents, [String class]) { |
| + final css = class == null ? '' : ' class="$class"'; |
| + return '<a href="${relativePath(href)}"$css>$contents</a>'; |
| } |
| - findMember(Type type) { |
| - final member = type.members[name]; |
| - 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; |
| + /** |
| + * 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; |
| } |
| - return member; |
| - } |
| + // For parameters, handle non-typedefed function types. |
| + if (paramName !== null) { |
| + final call = type.getCallMethod(); |
| + if (call != null) { |
| + annotateType(enclosingType, call.returnType); |
| + write(paramName); |
| - // See if it's a parameter of the current method. |
| - if (_currentMember != null) { |
| - for (final parameter in _currentMember.parameters) { |
| - if (parameter.name == name) { |
| - final element = new md.Element.text('span', name); |
| - element.attributes['class'] = 'param'; |
| - return element; |
| + docParamList(enclosingType, call); |
| + return; |
| } |
| } |
| + |
| + linkToType(enclosingType, type); |
| + |
| + write(' '); |
| + if (paramName !== null) write(paramName); |
| } |
| - // See if it's another member of the current type. |
| - if (_currentType != null) { |
| - final member = findMember(_currentType); |
| - if (member != null) { |
| - return makeLink(memberUrl(member)); |
| + /** 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; |
| } |
| - } |
| - // See if it's another type in the current library. |
| - if (_currentLibrary != null) { |
| - final type = _currentLibrary.types[name]; |
| - if (type != null) { |
| - return makeLink(typeUrl(type)); |
| + // 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 a top-level member in the current library. |
| - final member = findMember(_currentLibrary.topType); |
| - if (member != null) { |
| - return makeLink(memberUrl(member)); |
| + // See if it's an instantiation of a generic type. |
| + final typeArgs = type.typeArgsInOrder; |
| + if (typeArgs != null) { |
| + write('<'); |
| + bool first = true; |
| + for (final arg in typeArgs) { |
| + if (!first) write(', '); |
| + first = false; |
| + linkToType(enclosingType, arg); |
| + } |
| + write('>'); |
| } |
| } |
| - // TODO(rnystrom): Should also consider: |
| - // * Names imported by libraries this library imports. |
| - // * Type parameters of the enclosing type. |
| + /** 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), class: '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); |
| + } |
| + } |
| - return new md.Element.text('code', name); |
| -} |
| + final params = Strings.join(typeParams, ', '); |
| + return '${type.name}<$params>'; |
| + } |
| -/** |
| - * Documents the code contained within [span]. Will include the previous |
| - * Dartdoc associated with that span if found, and will include the syntax |
| - * highlighted code itself if desired. |
| - */ |
| -docCode(SourceSpan span, String extraMarkdown, [bool showCode = false]) { |
| - if (span == null) return; |
| - |
| - writeln('<div class="doc">'); |
| - final comment = findComment(span); |
| - if (comment != null) { |
| - writeln(md.markdownToHtml('${comment}\n\n${extraMarkdown}')); |
| - } else { |
| - writeln(md.markdownToHtml(extraMarkdown)); |
| + // See if it's an instantiation of a generic type. |
| + final typeArgs = type.typeArgsInOrder; |
| + if (typeArgs != null) { |
| + final args = Strings.join(map(typeArgs, (arg) => typeName(arg)), ', '); |
| + return '${type.genericType.name}<$args>'; |
| + } |
| + |
| + // Regular type. |
| + return type.name; |
| } |
| - if (includeSource && showCode) { |
| - writeln('<pre class="source">'); |
| - write(formatCode(span)); |
| - writeln('</pre>'); |
| + /** |
| + * Takes a string of Dart code and turns it into sanitized HTML. |
| + */ |
| + formatCode(SourceSpan span) { |
| + // Remove leading indentation to line up with first line. |
| + final column = getSpanColumn(span); |
| + final lines = span.text.split('\n'); |
| + // TODO(rnystrom): Dirty hack. |
| + for (final i = 1; i < lines.length; i++) { |
| + lines[i] = unindent(lines[i], column); |
| + } |
| + |
| + final code = Strings.join(lines, '\n'); |
| + |
| + // Syntax highlight. |
| + return classifySource(new SourceFile('', code)); |
| } |
| - writeln('</div>'); |
| -} |
| + /** |
| + * 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) { |
| + makeLink(String href) { |
| + final anchor = new md.Element.text('a', name); |
| + anchor.attributes['href'] = relativePath(href); |
| + anchor.attributes['class'] = 'crossref'; |
| + return anchor; |
| + } |
| -/** Finds the doc comment preceding the given source span, if there is one. */ |
| -findComment(SourceSpan span) => findCommentInFile(span.file, span.start); |
| + findMember(Type type) { |
| + final member = type.members[name]; |
| + if (member == null) return null; |
| -/** Finds the doc comment preceding the given source span, if there is one. */ |
| -findCommentInFile(SourceFile file, int position) { |
| - // Get the doc comments for this file. |
| - final fileComments = _comments.putIfAbsent(file.filename, |
| - () => parseDocComments(file)); |
| + // 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 fileComments[position]; |
| -} |
| + return member; |
| + } |
| -parseDocComments(SourceFile file) { |
| - final comments = new Map<int, String>(); |
| - |
| - final tokenizer = new Tokenizer(file, false); |
| - var lastComment = null; |
| - |
| - while (true) { |
| - final token = tokenizer.next(); |
| - if (token.kind == TokenKind.END_OF_FILE) break; |
| - |
| - if (token.kind == TokenKind.COMMENT) { |
| - final text = token.text; |
| - if (text.startsWith('/**')) { |
| - // Remember that we've encountered a doc comment. |
| - lastComment = stripComment(token.text); |
| - } else if (text.startsWith('///')) { |
| - var line = text.substring(3, text.length); |
| - // Allow a leading space. |
| - if (line.startsWith(' ')) line = line.substring(1, text.length); |
| - if (lastComment == null) { |
| - lastComment = line; |
| - } else { |
| - lastComment = '$lastComment$line'; |
| + // See if it's a parameter of the current method. |
| + if (_currentMember != null) { |
| + for (final parameter in _currentMember.parameters) { |
| + if (parameter.name == name) { |
| + final element = new md.Element.text('span', name); |
| + element.attributes['class'] = 'param'; |
| + return element; |
| } |
| } |
| - } else if (token.kind == TokenKind.WHITESPACE) { |
| - // Ignore whitespace tokens. |
| - } else if (token.kind == TokenKind.HASH) { |
| - // Look for #library() to find the library comment. |
| - final next = tokenizer.next(); |
| - if ((lastComment != null) && (next.kind == TokenKind.LIBRARY)) { |
| - comments[_libraryDoc] = lastComment; |
| - lastComment = null; |
| - } |
| - } else { |
| - if (lastComment != null) { |
| - // We haven't attached the last doc comment to something yet, so stick |
| - // it to this token. |
| - comments[token.start] = lastComment; |
| - lastComment = null; |
| - } |
| } |
| - } |
| - return comments; |
| -} |
| - |
| -/** |
| - * Takes a string of Dart code and turns it into sanitized HTML. |
| - */ |
| -formatCode(SourceSpan span) { |
| - // Remove leading indentation to line up with first line. |
| - final column = getSpanColumn(span); |
| - final lines = span.text.split('\n'); |
| - // TODO(rnystrom): Dirty hack. |
| - for (final i = 1; i < lines.length; i++) { |
| - lines[i] = unindent(lines[i], column); |
| - } |
| + // See if it's another member of the current type. |
| + if (_currentType != null) { |
| + final member = findMember(_currentType); |
| + if (member != null) { |
| + return makeLink(memberUrl(member)); |
| + } |
| + } |
| - final code = Strings.join(lines, '\n'); |
| + // See if it's another type in the current library. |
| + if (_currentLibrary != null) { |
| + final type = _currentLibrary.types[name]; |
| + if (type != null) { |
| + return makeLink(typeUrl(type)); |
| + } |
| - // Syntax highlight. |
| - return classifySource(new SourceFile('', code)); |
| -} |
| + // See if it's a top-level member in the current library. |
| + final member = findMember(_currentLibrary.topType); |
| + if (member != null) { |
| + return makeLink(memberUrl(member)); |
| + } |
| + } |
| -// TODO(rnystrom): Move into SourceSpan? |
| -int getSpanColumn(SourceSpan span) { |
| - final line = span.file.getLine(span.start); |
| - return span.file.getColumn(line, span.start); |
| -} |
| + // TODO(rnystrom): Should also consider: |
| + // * Names imported by libraries this library imports. |
| + // * Type parameters of the enclosing type. |
| -/** |
| - * Pulls the raw text out of a doc comment (i.e. removes the comment |
| - * characters). |
| - */ |
| -stripComment(comment) { |
| - StringBuffer buf = new StringBuffer(); |
| - |
| - for (final line in comment.split('\n')) { |
| - line = line.trim(); |
| - if (line.startsWith('/**')) line = line.substring(3, line.length); |
| - if (line.endsWith('*/')) line = line.substring(0, line.length - 2); |
| - line = line.trim(); |
| - if (line.startsWith('* ')) { |
| - line = line.substring(2, line.length); |
| - } else if (line.startsWith('*')) { |
| - line = line.substring(1, line.length); |
| - } |
| - |
| - buf.add(line); |
| - buf.add('\n'); |
| + return new md.Element.text('code', name); |
| } |
| - return buf.toString(); |
| + // TODO(rnystrom): Move into SourceSpan? |
| + int getSpanColumn(SourceSpan span) { |
| + final line = span.file.getLine(span.start); |
| + return span.file.getColumn(line, span.start); |
| + } |
| } |
| - |
| -/** Register a callback to add additional documentation to a type. */ |
| -addTypeDocumenter(TypeDocumenter fn) => _typeDocumenters.add(fn); |
| - |
| -/** Register a callback to add additional documentation to a method. */ |
| -addMethodDocumenter(MethodDocumenter fn) => _methodDocumenters.add(fn); |
| - |
| -/** Register a callback to add additional documentation to a field. */ |
| -addFieldDocumenter(FieldDocumenter fn) => _fieldDocumenters.add(fn); |