| // Copyright (c) 2019, 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. |
| |
| import 'package:analyzer/dart/element/element2.dart'; |
| import 'package:analyzer/file_system/file_system.dart'; |
| import 'package:dartdoc/src/dartdoc_options.dart'; |
| import 'package:dartdoc/src/model/comment_referable.dart'; |
| import 'package:dartdoc/src/model/kind.dart'; |
| import 'package:dartdoc/src/model/model.dart'; |
| import 'package:dartdoc/src/warnings.dart'; |
| |
| /// A subcategory of a package, containing elements tagged with `{@category}`. |
| class Category |
| with |
| Nameable, |
| Warnable, |
| CommentReferable, |
| Locatable, |
| MarkdownFileDocumentation, |
| LibraryContainer, |
| TopLevelContainer |
| implements Documentable { |
| /// The package in which this category is contained. |
| /// |
| /// All libraries in [libraries] must come from [package]. |
| @override |
| final Package package; |
| |
| final String? _name; |
| |
| @override |
| final DartdocOptionContext config; |
| |
| final Set<Class> _classes = {}; |
| |
| final Set<Class> _exceptions = {}; |
| |
| @override |
| final Set<TopLevelVariable> constants = {}; |
| |
| @override |
| final Set<Extension> extensions = {}; |
| |
| @override |
| final Set<ExtensionType> extensionTypes = {}; |
| |
| @override |
| final Set<Enum> enums = {}; |
| |
| @override |
| final Set<ModelFunction> functions = {}; |
| |
| @override |
| final Set<Mixin> mixins = {}; |
| |
| @override |
| final Set<TopLevelVariable> properties = {}; |
| |
| @override |
| final Set<Typedef> typedefs = {}; |
| |
| final CategoryDefinition _categoryDefinition; |
| |
| Category(this._name, this.package, this.config) |
| : _categoryDefinition = |
| config.categories.categoryDefinitions[_name.orDefault] ?? |
| CategoryDefinition(_name, null, null); |
| |
| Iterable<ExternalItem> get externalItems => _categoryDefinition.externalItems; |
| |
| void addClass(Class class_) { |
| if (class_.isErrorOrException) { |
| _exceptions.add(class_); |
| } else { |
| _classes.add(class_); |
| } |
| } |
| |
| @override |
| Element2? get element => null; |
| |
| @override |
| String get name => _categoryDefinition.displayName; |
| |
| @override |
| String get sortKey => _name.orDefault; |
| |
| @override |
| List<String> get containerOrder => config.categoryOrder; |
| |
| @override |
| String get enclosingName => package.name; |
| |
| @override |
| PackageGraph get packageGraph => package.packageGraph; |
| |
| @override |
| List<Locatable> get documentationFrom => [this]; |
| |
| @override |
| DocumentLocation get documentedWhere => package.documentedWhere; |
| |
| @override |
| late final bool isDocumented = |
| documentedWhere != DocumentLocation.missing && documentationFile != null; |
| |
| String get fileName { |
| assert(_name != null); |
| return '$_name-topic.html'; |
| } |
| |
| String get filePath => 'topics/$fileName'; |
| |
| /// Prior to dartdoc 8.3.4 the `displayName` was used in the file path |
| /// for category pages. We now create a redirect file here instead. |
| String get redirectFilePath => 'topics/$name-topic.html'; |
| |
| @override |
| String? get href => isCanonical ? '${package.baseHref}$filePath' : null; |
| |
| @override |
| String? get aboveSidebarPath => null; |
| |
| @override |
| String? get belowSidebarPath => null; |
| |
| String get categoryLabel { |
| final buffer = StringBuffer('<span class="category '); |
| buffer.writeAll(name.toLowerCase().split(' '), '-'); |
| buffer.write(' cp-'); |
| buffer.write(categoryIndex); |
| |
| if (isDocumented) { |
| buffer.write(' linked'); |
| } |
| |
| buffer.write('"'); // Wrap up the class list and begin title |
| buffer.write(' title="This is part of the '); |
| buffer.write(name); |
| buffer.write(' '); |
| buffer.write(kind); |
| buffer.write('.">'); // Wrap up the title |
| |
| buffer.write(linkedName); |
| buffer.write('</span>'); |
| |
| return buffer.toString(); |
| } |
| |
| String get linkedName { |
| final unbrokenName = name.replaceAll(' ', ' '); |
| if (isDocumented) { |
| final href = this.href; |
| if (href == null) { |
| throw StateError("Requesting the 'linkedName' of a non-canonical " |
| "category: '$name'"); |
| } |
| return '<a href="$href">$unbrokenName</a>'; |
| } else { |
| return unbrokenName; |
| } |
| } |
| |
| /// The position in the container order for this category. |
| int get categoryIndex => package.categories.indexOf(this); |
| |
| @override |
| bool get isCanonical => |
| config.categories.categoryDefinitions.containsKey(sortKey); |
| |
| @override |
| Kind get kind => Kind.topic; |
| |
| @override |
| File? get documentationFile { |
| var documentationMarkdown = _categoryDefinition.documentationMarkdown; |
| if (documentationMarkdown != null) { |
| return config.resourceProvider.getFile(documentationMarkdown); |
| } |
| return null; |
| } |
| |
| @override |
| Iterable<Class> get classes => _classes; |
| |
| @override |
| Iterable<Class> get exceptions => _exceptions; |
| |
| @override |
| Map<String, CommentReferable> get referenceChildren => const {}; |
| |
| @override |
| Iterable<CommentReferable> get referenceParents => const []; |
| } |
| |
| extension on String? { |
| String get orDefault => this ?? '<default>'; |
| } |
| |
| /// A external link for an item in a dartdoc category. |
| /// |
| /// This is a name, url, and optional documentation text. |
| class ExternalItem { |
| final String name; |
| final String url; |
| final String docs; |
| |
| ExternalItem({ |
| required this.name, |
| required this.url, |
| required String? docs, |
| }) : docs = docs ?? ''; |
| |
| @override |
| String toString() => '$name $url'; |
| } |