diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e1c70b337..b3d3b75df7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ ## unreleased * [enhancement] added a `--favicon` option to specify a favicon to use for the generated docs +* [enhancement] added a `--use-categories` flag to groups libraries into source + packages in the overview page and the left-hand side navigation panel ## 0.9.3+1 * [bug] fix an issue with including duplicated libraries diff --git a/bin/dartdoc.dart b/bin/dartdoc.dart index 150f02cfcc..b3c8ce860c 100644 --- a/bin/dartdoc.dart +++ b/bin/dartdoc.dart @@ -116,7 +116,7 @@ main(List arguments) async { var generators = await initGenerators( url, headerFilePaths, footerFilePaths, args['rel-canonical-prefix'], - faviconPath: args['favicon']); + faviconPath: args['favicon'], useCategories: args['use-categories']); for (var generator in generators) { generator.onFileCreated.listen(_onProgress); @@ -207,6 +207,10 @@ ArgParser _createArgsParser() { help: 'Show source code blocks', negatable: true, defaultsTo: true); parser.addOption('favicon', help: 'A path to a favicon for the generated docs'); + parser.addFlag('use-categories', + help: 'Group libraries from the same package into categories', + negatable: false, + defaultsTo: false); return parser; } diff --git a/lib/dartdoc.dart b/lib/dartdoc.dart index 1a54e4fc35..78f4119000 100644 --- a/lib/dartdoc.dart +++ b/lib/dartdoc.dart @@ -46,7 +46,7 @@ final String defaultOutDir = p.join('doc', 'api'); /// Initialize and setup the generators. Future> initGenerators(String url, List headerFilePaths, List footerFilePaths, String relCanonicalPrefix, - {String faviconPath}) async { + {String faviconPath, bool useCategories: false}) async { return [ await HtmlGenerator.create( url: url, @@ -54,7 +54,8 @@ Future> initGenerators(String url, List headerFilePaths, footers: footerFilePaths, relCanonicalPrefix: relCanonicalPrefix, toolVersion: version, - faviconPath: faviconPath) + faviconPath: faviconPath, + useCategories: useCategories) ]; } diff --git a/lib/src/html/html_generator.dart b/lib/src/html/html_generator.dart index 9b93482850..3554b649d3 100644 --- a/lib/src/html/html_generator.dart +++ b/lib/src/html/html_generator.dart @@ -40,6 +40,7 @@ class HtmlGenerator extends Generator { final Templates _templates; final String _toolVersion; final String faviconPath; + final bool useCategories; final StreamController _onFileCreated = new StreamController(sync: true); @@ -53,7 +54,8 @@ class HtmlGenerator extends Generator { List footers, String relCanonicalPrefix, String toolVersion, - String faviconPath}) async { + String faviconPath, + bool useCategories: false}) async { var templates = await Templates.create(headerPaths: headers, footerPaths: footers); @@ -62,17 +64,17 @@ class HtmlGenerator extends Generator { } return new HtmlGenerator._(url, relCanonicalPrefix, templates, toolVersion, - faviconPath: faviconPath); + faviconPath: faviconPath, useCategories: useCategories); } HtmlGenerator._( this._url, this._relCanonicalPrefix, this._templates, this._toolVersion, - {this.faviconPath}); + {this.faviconPath, this.useCategories}); Future generate(Package package, Directory out) { return new HtmlGeneratorInstance(_toolVersion, _url, _templates, package, out, _onFileCreated, _relCanonicalPrefix, - faviconPath: faviconPath) + faviconPath: faviconPath, useCategories: useCategories) .generate(); } } diff --git a/lib/src/html/html_generator_instance.dart b/lib/src/html/html_generator_instance.dart index 3e4fb364b1..e7e01098ab 100644 --- a/lib/src/html/html_generator_instance.dart +++ b/lib/src/html/html_generator_instance.dart @@ -25,10 +25,11 @@ class HtmlGeneratorInstance implements HtmlOptions { final String relCanonicalPrefix; final String toolVersion; final String faviconPath; + final bool useCategories; HtmlGeneratorInstance(this.toolVersion, this.url, this._templates, this.package, this.out, this._onFileCreated, this.relCanonicalPrefix, - {this.faviconPath}); + {this.faviconPath, this.useCategories}); Future generate() async { if (!out.existsSync()) out.createSync(); @@ -134,7 +135,7 @@ class HtmlGeneratorInstance implements HtmlOptions { void generatePackage() { stdout.write('documenting ${package.name}'); - TemplateData data = new PackageTemplateData(this, package); + TemplateData data = new PackageTemplateData(this, package, useCategories); _build('index.html', _templates.indexTemplate, data); } @@ -148,7 +149,8 @@ class HtmlGeneratorInstance implements HtmlOptions { "documentation comments"); } - TemplateData data = new LibraryTemplateData(this, package, lib); + TemplateData data = + new LibraryTemplateData(this, package, lib, useCategories); _build(path.join(lib.dirName, '${lib.fileName}'), _templates.libraryTemplate, data); diff --git a/lib/src/html/template_data.dart b/lib/src/html/template_data.dart index 3c0fab3293..2880b15188 100644 --- a/lib/src/html/template_data.dart +++ b/lib/src/html/template_data.dart @@ -68,10 +68,12 @@ abstract class TemplateData { } class PackageTemplateData extends TemplateData { - PackageTemplateData(HtmlOptions htmlOptions, Package package) + PackageTemplateData( + HtmlOptions htmlOptions, Package package, this.useCategories) : super(htmlOptions, package); bool get includeVersion => true; + final bool useCategories; List get navLinks => []; String get title => '${package.name} - Dart API docs'; Package get self => package; @@ -90,11 +92,13 @@ class PackageTemplateData extends TemplateData { class LibraryTemplateData extends TemplateData { final Library library; - LibraryTemplateData(HtmlOptions htmlOptions, Package package, this.library) + LibraryTemplateData(HtmlOptions htmlOptions, Package package, this.library, + this.useCategories) : super(htmlOptions, package); String get title => '${library.name} library - Dart API'; String get documentation => library.documentation; + final bool useCategories; String get htmlBase => '..'; String get metaDescription => '${library.name} library API docs, for the Dart programming language.'; diff --git a/lib/src/model.dart b/lib/src/model.dart index abdc96efe1..cd282dccec 100644 --- a/lib/src/model.dart +++ b/lib/src/model.dart @@ -6,6 +6,7 @@ library dartdoc.models; import 'dart:convert'; +import 'dart:io'; import 'package:analyzer/dart/ast/ast.dart' show AnnotatedNode, Annotation, Declaration; @@ -16,6 +17,7 @@ import 'package:analyzer/src/generated/resolver.dart' import 'package:analyzer/src/generated/source_io.dart'; import 'package:analyzer/src/generated/utilities_dart.dart' show ParameterKind; import 'package:collection/collection.dart'; +import 'package:path/path.dart' as p; import 'package:quiver/core.dart' show hash3; import 'config.dart'; @@ -921,6 +923,7 @@ class Library extends ModelElement { List _variables; Namespace _exportedNamespace; String _name; + String _packageName; factory Library(LibraryElement element, Package package) { String key = element == null ? 'null' : element.name; @@ -1020,6 +1023,21 @@ class Library extends ModelElement { @override String get href => '$dirName/$fileName'; + String get packageName { + if (_packageName == null) { + String sourcePath = _library.source.fullName; + File file = new File(sourcePath); + if (file.existsSync()) { + _packageName = _getPackageName(file.parent); + if (_packageName == null) _packageName = ''; + } else { + _packageName = ''; + } + } + + return _packageName; + } + bool get isAnonymous => element.name == null || element.name.isEmpty; bool get isDocumented => oneLineDoc.isNotEmpty; @@ -1143,6 +1161,20 @@ class Library extends ModelElement { return _variables; } + + static String _getPackageName(Directory dir) { + if (!dir.existsSync() || !dir.path.contains(Platform.pathSeparator)) { + return null; + } + + File pubspec = new File(p.join(dir.path, 'pubspec.yaml')); + if (pubspec.existsSync()) { + PackageMeta meta = new PackageMeta.fromDir(dir); + return meta.name; + } else { + return _getPackageName(dir.parent); + } + } } class Method extends ModelElement @@ -1718,6 +1750,25 @@ class Package implements Nameable, Documentable { List get libraries => _libraries; + List get categories { + Map result = {}; + + for (Library library in _libraries) { + String name = ''; + + if (library.name.startsWith('dart:')) { + name = 'Dart Core'; + } else { + name = library.packageName; + } + + if (!result.containsKey(name)) result[name] = new PackageCategory(name); + result[name]._libraries.add(library); + } + + return result.values.toList()..sort(); + } + String get name => packageMeta.name; String get oneLineDoc => ''; @@ -1780,6 +1831,17 @@ class Package implements Nameable, Documentable { } } +class PackageCategory implements Comparable { + final String name; + final List _libraries = []; + + PackageCategory(this.name); + + List get libraries => _libraries; + + int compareTo(PackageCategory other) => name.compareTo(other.name); +} + class Parameter extends ModelElement implements EnclosedElement { Parameter(ParameterElement element, Library library) : super(element, library) { diff --git a/lib/templates/index.html b/lib/templates/index.html index 5a9c303b4a..a5b0f42e7f 100644 --- a/lib/templates/index.html +++ b/lib/templates/index.html @@ -3,38 +3,67 @@
- {{#package}} {{>documentation}} {{/package}} -
-

Libraries

-
- {{#package.libraries}} -
- {{{ linkedName }}} -
-
- {{#isDocumented}} -

- {{{ oneLineDoc }}} - {{>has_more_docs}} -

- {{/isDocumented}} -
- {{/package.libraries}} -
-
+ {{#useCategories}} + {{#package.categories}} +
+

{{name}}

+
+ {{#libraries}} +
+ {{{ linkedName }}} +
+
+ {{#isDocumented}}

{{{ oneLineDoc }}}

{{/isDocumented}} +
+ {{/libraries}} +
+
+ {{/package.categories}} + + {{/useCategories}} + + {{^useCategories}} +
+

Libraries

+
+ {{#package.libraries}} +
+ {{{ linkedName }}} +
+
+ {{#isDocumented}}

{{{ oneLineDoc }}}

{{/isDocumented}} +
+ {{/package.libraries}} +
+
+ {{/useCategories}}
diff --git a/lib/templates/library.html b/lib/templates/library.html index 21476c2ed2..813c5f3db3 100644 --- a/lib/templates/library.html +++ b/lib/templates/library.html @@ -5,12 +5,26 @@
{{name}}
{{/navLinks}} + {{#useCategories}}
    -
  1. Libraries
  2. + {{#package.categories}} +
  3. {{name}}
  4. + {{#libraries}} +
  5. {{{linkedName}}}
  6. + {{/libraries}} + {{/package.categories}} + +
+ {{/useCategories}} + + {{^useCategories}} +
    +
  1. Libraries
  2. {{#package.libraries}}
  3. {{{linkedName}}}
  4. {{/package.libraries}}
+ {{/useCategories}}
diff --git a/test/model_test.dart b/test/model_test.dart index 67d95abeb9..7e12e5d8d4 100644 --- a/test/model_test.dart +++ b/test/model_test.dart @@ -47,6 +47,14 @@ void main() { expect(package.libraries, hasLength(6)); }); + test('categories', () { + expect(package.categories, hasLength(1)); + + PackageCategory category = package.categories.first; + expect(category.name, 'test_package'); + expect(category.libraries, hasLength(6)); + }); + test('is documented in library', () { expect(package.isDocumented(exLibrary.element), isTrue); }); @@ -121,6 +129,10 @@ void main() { expect(exLibrary.name, 'ex'); }); + test('packageName', () { + expect(exLibrary.packageName, 'test_package'); + }); + test('has a fully qualified name', () { expect(exLibrary.fullyQualifiedName, 'ex'); }); diff --git a/testing/test_package_docs/anonymous_library/anonymous_library-library.html b/testing/test_package_docs/anonymous_library/anonymous_library-library.html index d6c425a62f..0fb776c1d4 100644 --- a/testing/test_package_docs/anonymous_library/anonymous_library-library.html +++ b/testing/test_package_docs/anonymous_library/anonymous_library-library.html @@ -69,8 +69,9 @@