Add support for extension methods (#2012)
* Add suport for extensions
* Add options file to test package
* remove path dependencies
* Address review comments
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4d4a40c..41f08e0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.28.6-dev
+* Support for 0.38.2 version of `package:analyzer`.
+* Support generating docs for extension methods (#2001).
+
## 0.28.5
* Support the latest version of `package:analyzer`.
* Fix hyperlinks to overriden methods (#1994).
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 5bbf4f3..9ab324b 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -1,6 +1,9 @@
include: package:pedantic/analysis_options.1.8.0.yaml
analyzer:
+ # enable for analysis on test package sources.
+ enable-experiment:
+ - extension-methods
exclude:
- 'doc/**'
- 'lib/src/third_party/pkg/**'
diff --git a/dartdoc_options.yaml b/dartdoc_options.yaml
index 289fcaf..7a9b637 100644
--- a/dartdoc_options.yaml
+++ b/dartdoc_options.yaml
@@ -1,4 +1,4 @@
dartdoc:
linkToSource:
root: '.'
- uriTemplate: 'https://github.com/dart-lang/dartdoc/blob/v0.28.5/%f%#L%l%'
+ uriTemplate: 'https://github.com/dart-lang/dartdoc/blob/v0.28.6-dev/%f%#L%l%'
diff --git a/lib/src/element_type.dart b/lib/src/element_type.dart
index 0d2598c..e03e188 100644
--- a/lib/src/element_type.dart
+++ b/lib/src/element_type.dart
@@ -191,7 +191,7 @@
/// would ordinarily do.
@override
bool get isPublic {
- Class canonicalClass =
+ Container canonicalClass =
element.packageGraph.findCanonicalModelElementFor(element.element) ??
element;
return canonicalClass.isPublic;
diff --git a/lib/src/html/html_generator_instance.dart b/lib/src/html/html_generator_instance.dart
index e542532..00a8cbe 100644
--- a/lib/src/html/html_generator_instance.dart
+++ b/lib/src/html/html_generator_instance.dart
@@ -171,6 +171,37 @@
}
}
+ for (var extension in filterNonPublic(lib.extensions)) {
+ generateExtension(_packageGraph, lib, extension);
+
+ for (var constant in filterNonDocumented(extension.constants)) {
+ generateConstant(_packageGraph, lib, extension, constant);
+ }
+
+ for (var property
+ in filterNonDocumented(extension.staticProperties)) {
+ generateProperty(_packageGraph, lib, extension, property);
+ }
+
+ for (var method
+ in filterNonDocumented(extension.allPublicInstanceMethods)) {
+ generateMethod(_packageGraph, lib, extension, method);
+ }
+
+ for (var method in filterNonDocumented(extension.staticMethods)) {
+ generateMethod(_packageGraph, lib, extension, method);
+ }
+
+ for (var operator in filterNonDocumented(extension.allOperators)) {
+ generateMethod(_packageGraph, lib, extension, operator);
+ }
+
+ for (var property
+ in filterNonDocumented(extension.allInstanceFields)) {
+ generateProperty(_packageGraph, lib, extension, property);
+ }
+ }
+
for (var mixin in filterNonDocumented(lib.mixins)) {
generateMixins(_packageGraph, lib, mixin);
for (var constructor in filterNonDocumented(mixin.constructors)) {
@@ -275,6 +306,13 @@
_build(path.joinAll(clazz.href.split('/')), _templates.classTemplate, data);
}
+ void generateExtension(
+ PackageGraph packageGraph, Library lib, Extension ext) {
+ TemplateData data = ExtensionTemplateData(_options, packageGraph, lib, ext);
+ _build(
+ path.joinAll(ext.href.split('/')), _templates.extensionTemplate, data);
+ }
+
void generateMixins(PackageGraph packageGraph, Library lib, Mixin mixin) {
TemplateData data = MixinTemplateData(_options, packageGraph, lib, mixin);
_build(path.joinAll(mixin.href.split('/')), _templates.mixinTemplate, data);
@@ -305,7 +343,7 @@
}
void generateMethod(
- PackageGraph packageGraph, Library lib, Class clazz, Method method) {
+ PackageGraph packageGraph, Library lib, Container clazz, Method method) {
TemplateData data =
MethodTemplateData(_options, packageGraph, lib, clazz, method);
@@ -314,7 +352,7 @@
}
void generateConstant(
- PackageGraph packageGraph, Library lib, Class clazz, Field property) {
+ PackageGraph packageGraph, Library lib, Container clazz, Field property) {
TemplateData data =
ConstantTemplateData(_options, packageGraph, lib, clazz, property);
@@ -323,7 +361,7 @@
}
void generateProperty(
- PackageGraph packageGraph, Library lib, Class clazz, Field property) {
+ PackageGraph packageGraph, Library lib, Container clazz, Field property) {
TemplateData data =
PropertyTemplateData(_options, packageGraph, lib, clazz, property);
diff --git a/lib/src/html/template_data.dart b/lib/src/html/template_data.dart
index fe1b058..271ce70 100644
--- a/lib/src/html/template_data.dart
+++ b/lib/src/html/template_data.dart
@@ -185,6 +185,34 @@
}
}
+/// Base template data class for [Extension].
+class ExtensionTemplateData<T extends Extension> extends TemplateData<T> {
+ final T extension;
+ final Library library;
+
+ ExtensionTemplateData(HtmlOptions htmlOptions, PackageGraph packageGraph,
+ this.library, this.extension)
+ : super(htmlOptions, packageGraph);
+
+ @override
+ T get self => extension;
+
+ @override
+ String get title =>
+ '${extension.name} ${extension.kind} - ${library.name} library - Dart API';
+ @override
+ String get metaDescription =>
+ 'API docs for the ${extension.name} ${extension.kind} from the '
+ '${library.name} library, for the Dart programming language.';
+
+ @override
+ String get layoutTitle => _layoutTitle(extension.name, extension.kind, false);
+ @override
+ List get navLinks => [packageGraph.defaultPackage, library];
+ @override
+ String get htmlBase => '..';
+}
+
class ConstructorTemplateData extends TemplateData<Constructor> {
final Library library;
final Class clazz;
@@ -255,23 +283,26 @@
class MethodTemplateData extends TemplateData<Method> {
final Library library;
final Method method;
- final Class clazz;
+ final Container clazz;
+ String container;
MethodTemplateData(HtmlOptions htmlOptions, PackageGraph packageGraph,
this.library, this.clazz, this.method)
- : super(htmlOptions, packageGraph);
+ : super(htmlOptions, packageGraph) {
+ container = clazz.isClass ? 'class' : 'extension';
+ }
@override
Method get self => method;
@override
- String get title => '${method.name} method - ${clazz.name} class - '
+ String get title => '${method.name} method - ${clazz.name} ${container} - '
'${library.name} library - Dart API';
@override
String get layoutTitle => _layoutTitle(
method.nameWithGenerics, method.fullkind, method.isDeprecated);
@override
String get metaDescription =>
- 'API docs for the ${method.name} method from the ${clazz.name} class, '
+ 'API docs for the ${method.name} method from the ${clazz.name} ${container}, '
'for the Dart programming language.';
@override
List get navLinks => [packageGraph.defaultPackage, library];
@@ -283,25 +314,28 @@
class PropertyTemplateData extends TemplateData<Field> {
final Library library;
- final Class clazz;
+ final Container clazz;
final Field property;
+ String container;
PropertyTemplateData(HtmlOptions htmlOptions, PackageGraph packageGraph,
this.library, this.clazz, this.property)
- : super(htmlOptions, packageGraph);
+ : super(htmlOptions, packageGraph) {
+ container = clazz.isClass ? 'class' : 'extension';
+ }
@override
Field get self => property;
@override
- String get title => '${property.name} $type - ${clazz.name} class - '
+ String get title => '${property.name} $type - ${clazz.name} ${container} - '
'${library.name} library - Dart API';
@override
String get layoutTitle =>
_layoutTitle(property.name, type, property.isDeprecated);
@override
String get metaDescription =>
- 'API docs for the ${property.name} $type from the ${clazz.name} class, '
+ 'API docs for the ${property.name} $type from the ${clazz.name} ${container}, '
'for the Dart programming language.';
@override
List get navLinks => [packageGraph.defaultPackage, library];
@@ -315,7 +349,7 @@
class ConstantTemplateData extends PropertyTemplateData {
ConstantTemplateData(HtmlOptions htmlOptions, PackageGraph packageGraph,
- Library library, Class clazz, Field property)
+ Library library, Container clazz, Field property)
: super(htmlOptions, packageGraph, library, clazz, property);
@override
diff --git a/lib/src/html/templates.dart b/lib/src/html/templates.dart
index ca8e31b..aa78da0 100644
--- a/lib/src/html/templates.dart
+++ b/lib/src/html/templates.dart
@@ -18,6 +18,7 @@
'categorization',
'class',
'constant',
+ 'extension',
'footer',
'head',
'library',
@@ -31,6 +32,7 @@
'sidebar_for_class',
'sidebar_for_category',
'sidebar_for_enum',
+ 'sidebar_for_extension',
'source_code',
'source_link',
'sidebar_for_library',
@@ -45,6 +47,7 @@
'constant.html',
'constructor.html',
'enum.html',
+ 'extension.html',
'function.html',
'index.html',
'library.html',
@@ -65,7 +68,6 @@
List<String> headerPaths,
List<String> footerPaths,
List<String> footerTextPaths) async {
-
headerPaths ??= [];
footerPaths ??= [];
footerTextPaths ??= [];
@@ -75,8 +77,8 @@
void replacePlaceholder(String key, String placeholder, List<String> paths) {
var template = partials[key];
if (template != null && paths != null && paths.isNotEmpty) {
- String replacement = paths.map((p) => File(p).readAsStringSync())
- .join('\n');
+ String replacement =
+ paths.map((p) => File(p).readAsStringSync()).join('\n');
template = template.replaceAll(placeholder, replacement);
partials[key] = template;
}
@@ -140,6 +142,7 @@
class Templates {
final Template categoryTemplate;
final Template classTemplate;
+ final Template extensionTemplate;
final Template enumTemplate;
final Template constantTemplate;
final Template constructorTemplate;
@@ -164,8 +167,7 @@
footerTextPaths: footerTextPaths);
}
- static Future<Templates> fromDirectory(
- Directory dir,
+ static Future<Templates> fromDirectory(Directory dir,
{List<String> headerPaths,
List<String> footerPaths,
List<String> footerTextPaths}) async {
@@ -185,13 +187,12 @@
}
}
- static Future<Templates> _create(
- _TemplatesLoader templatesLoader,
+ static Future<Templates> _create(_TemplatesLoader templatesLoader,
{List<String> headerPaths,
List<String> footerPaths,
List<String> footerTextPaths}) async {
- var partials =
- await _loadPartials(templatesLoader, headerPaths, footerPaths, footerTextPaths);
+ var partials = await _loadPartials(
+ templatesLoader, headerPaths, footerPaths, footerTextPaths);
Template _partial(String name) {
String partial = partials[name];
@@ -202,7 +203,8 @@
}
Future<Template> _loadTemplate(String templatePath) async {
- String templateContents = await templatesLoader.loadTemplate(templatePath);
+ String templateContents =
+ await templatesLoader.loadTemplate(templatePath);
return Template(templateContents, partialResolver: _partial);
}
@@ -210,6 +212,7 @@
var libraryTemplate = await _loadTemplate('library.html');
var categoryTemplate = await _loadTemplate('category.html');
var classTemplate = await _loadTemplate('class.html');
+ var extensionTemplate = await _loadTemplate('extension.html');
var enumTemplate = await _loadTemplate('enum.html');
var functionTemplate = await _loadTemplate('function.html');
var methodTemplate = await _loadTemplate('method.html');
@@ -229,6 +232,7 @@
categoryTemplate,
libraryTemplate,
classTemplate,
+ extensionTemplate,
enumTemplate,
functionTemplate,
methodTemplate,
@@ -247,6 +251,7 @@
this.categoryTemplate,
this.libraryTemplate,
this.classTemplate,
+ this.extensionTemplate,
this.enumTemplate,
this.functionTemplate,
this.methodTemplate,
diff --git a/lib/src/markdown_processor.dart b/lib/src/markdown_processor.dart
index aac01e2..486334b 100644
--- a/lib/src/markdown_processor.dart
+++ b/lib/src/markdown_processor.dart
@@ -183,7 +183,7 @@
// Calculate a class hint for findCanonicalModelElementFor.
ModelElement _getPreferredClass(ModelElement modelElement) {
if (modelElement is EnclosedElement &&
- (modelElement as EnclosedElement).enclosingElement is Class) {
+ (modelElement as EnclosedElement).enclosingElement is Container) {
return (modelElement as EnclosedElement).enclosingElement;
} else if (modelElement is Class) {
return modelElement;
@@ -204,7 +204,7 @@
// Try expensive not-scoped lookup.
if (refModelElement == null && element is ModelElement) {
- Class preferredClass = _getPreferredClass(element);
+ Container preferredClass = _getPreferredClass(element);
refModelElement =
_MarkdownCommentReference(codeRef, element, commentRefs, preferredClass)
.computeReferredElement();
@@ -692,7 +692,7 @@
}
// Add a result, but make it canonical.
- void _addCanonicalResult(ModelElement modelElement, Class tryClass) {
+ void _addCanonicalResult(ModelElement modelElement, Container tryClass) {
results.add(packageGraph.findCanonicalModelElementFor(modelElement.element,
preferredClass: tryClass));
}
diff --git a/lib/src/model.dart b/lib/src/model.dart
index ebba432..eed06ba 100644
--- a/lib/src/model.dart
+++ b/lib/src/model.dart
@@ -161,8 +161,8 @@
bool get isInherited;
bool _canonicalEnclosingClassIsSet = false;
- Class _canonicalEnclosingClass;
- Class _definingEnclosingClass;
+ Container _canonicalEnclosingClass;
+ Container _definingEnclosingClass;
ModelElement get definingEnclosingElement {
if (_definingEnclosingClass == null) {
@@ -174,12 +174,21 @@
@override
ModelElement _buildCanonicalModelElement() {
- return canonicalEnclosingElement?.allCanonicalModelElements?.firstWhere(
- (m) => m.name == name && m.isPropertyAccessor == isPropertyAccessor,
- orElse: () => null);
+ if (canonicalEnclosingElement is Extension) {
+ return this;
+ }
+ if (canonicalEnclosingElement is Class) {
+ return (canonicalEnclosingElement as Class)
+ ?.allCanonicalModelElements
+ ?.firstWhere(
+ (m) =>
+ m.name == name && m.isPropertyAccessor == isPropertyAccessor,
+ orElse: () => null);
+ }
+ return null;
}
- Class get canonicalEnclosingElement {
+ Container get canonicalEnclosingElement {
Element searchElement = element;
if (!_canonicalEnclosingClassIsSet) {
if (isInherited) {
@@ -222,7 +231,8 @@
definingEnclosingElement.isPublic) {
assert(definingEnclosingElement == _canonicalEnclosingClass);
}
- } else {
+ } else if (enclosingElement is! Extension ||
+ (enclosingElement is Extension && enclosingElement.isDocumented)) {
_canonicalEnclosingClass =
packageGraph.findCanonicalModelElementFor(enclosingElement.element);
}
@@ -275,6 +285,10 @@
//
// We use canonical elements here where possible to deal with reexports
// as seen in Flutter.
+ if (enclosingElement is Extension) {
+ _isOverride = false;
+ return _isOverride;
+ }
Class enclosingCanonical = enclosingElement;
if (enclosingElement is ModelElement) {
enclosingCanonical =
@@ -355,7 +369,7 @@
bool get isInherited => _isInherited;
@override
- Class get enclosingElement {
+ Container get enclosingElement {
if (_enclosingElement == null) {
_enclosingElement = super.enclosingElement;
}
@@ -582,27 +596,144 @@
String get kind => 'mixin';
}
-class Class extends ModelElement
+// Can be either a Class or Extension, used in the package graph and template data.
+// Aggregates some of the common getters.
+abstract class Container extends ModelElement {
+ List<Method> _allMethods;
+ List<Field> _constants;
+ List<Operator> _operators;
+ List<Method> _staticMethods;
+ List<Method> _instanceMethods;
+ List<Field> _fields;
+ List<Field> _staticFields;
+ List<Field> _instanceFields;
+ List<TypeParameter> _typeParameters;
+
+ Container(Element element, Library library, PackageGraph packageGraph)
+ : super(element, library, packageGraph, null);
+
+ bool get isClass => element is ClassElement;
+
+ bool get isExtension => element is ExtensionElement;
+
+ List<Method> get _methods => _allMethods;
+
+ List<Method> get instanceMethods {
+ if (_instanceMethods != null) return _instanceMethods;
+
+ _instanceMethods = _methods
+ .where((m) => !m.isStatic && !m.isOperator)
+ .toList(growable: false)
+ ..sort(byName);
+ return _instanceMethods;
+ }
+
+ bool get hasPublicMethods => filterNonPublic(instanceMethods).isNotEmpty;
+
+ Iterable<Method> get allPublicInstanceMethods =>
+ filterNonPublic(instanceMethods);
+
+ List<Method> get staticMethods {
+ if (_staticMethods != null) return _staticMethods;
+
+ _staticMethods = _methods.where((m) => m.isStatic).toList(growable: false)
+ ..sort(byName);
+
+ return _staticMethods;
+ }
+
+ bool get hasPublicStaticMethods => filterNonPublic(staticMethods).isNotEmpty;
+
+ Iterable<Method> get publicStaticMethods => filterNonPublic(staticMethods);
+
+ List<Operator> get operators {
+ if (_operators != null) return _operators;
+ _operators = _methods
+ .where((m) => m.isOperator)
+ .cast<Operator>()
+ .toList(growable: false)
+ ..sort(byName);
+ return _operators;
+ }
+
+ Iterable<Operator> get allOperators => operators;
+
+ bool get hasPublicOperators => publicOperators.isNotEmpty;
+
+ Iterable<Operator> get allPublicOperators => filterNonPublic(allOperators);
+
+ Iterable<Operator> get publicOperators => filterNonPublic(operators);
+
+ List<Field> get _allFields => [];
+
+ List<Field> get staticProperties {
+ if (_staticFields != null) return _staticFields;
+ _staticFields = _allFields
+ .where((f) => f.isStatic && !f.isConst)
+ .toList(growable: false)
+ ..sort(byName);
+
+ return _staticFields;
+ }
+
+ Iterable<Field> get publicStaticProperties =>
+ filterNonPublic(staticProperties);
+
+ bool get hasPublicStaticProperties => publicStaticProperties.isNotEmpty;
+
+ List<Field> get instanceProperties {
+ if (_instanceFields != null) return _instanceFields;
+ _instanceFields = _allFields
+ .where((f) => !f.isStatic && !f.isInherited && !f.isConst)
+ .toList(growable: false)
+ ..sort(byName);
+
+ return _instanceFields;
+ }
+
+ Iterable<Field> get publicInstanceProperties =>
+ filterNonPublic(instanceProperties);
+
+ bool get hasPublicProperties => publicInstanceProperties.isNotEmpty;
+
+ Iterable<Field> get allInstanceFields => instanceProperties;
+
+ Iterable<Field> get allPublicInstanceProperties =>
+ filterNonPublic(allInstanceFields);
+
+ bool isInheritingFrom(Container other) => false;
+
+ List<Field> get constants {
+ if (_constants != null) return _constants;
+ _constants = _allFields.where((f) => f.isConst).toList(growable: false)
+ ..sort(byName);
+
+ return _constants;
+ }
+
+ Iterable<Field> get publicConstants => filterNonPublic(constants);
+
+ bool get hasPublicConstants => publicConstants.isNotEmpty;
+
+ Iterable<Accessor> get allAccessors => quiver.concat([
+ allInstanceFields.expand((f) => f.allAccessors),
+ constants.map((c) => c.getter)
+ ]);
+}
+
+class Class extends Container
with TypeParameters, Categorization
implements EnclosedElement {
List<DefinedElementType> _mixins;
DefinedElementType supertype;
List<DefinedElementType> _interfaces;
List<Constructor> _constructors;
- List<Method> _allMethods;
- List<Operator> _operators;
List<Operator> _inheritedOperators;
List<Method> _inheritedMethods;
- List<Method> _staticMethods;
- List<Method> _instanceMethods;
- List<Field> _fields;
- List<Field> _staticFields;
- List<Field> _constants;
- List<Field> _instanceFields;
List<Field> _inheritedProperties;
Class(ClassElement element, Library library, PackageGraph packageGraph)
- : super(element, library, packageGraph, null) {
+ : super(element, library, packageGraph) {
packageGraph.specialClasses.addSpecial(this);
_mixins = _cls.mixins
.map((f) {
@@ -634,44 +765,27 @@
Iterable<Method> get allInstanceMethods =>
quiver.concat([instanceMethods, inheritedMethods]);
+ @override
Iterable<Method> get allPublicInstanceMethods =>
filterNonPublic(allInstanceMethods);
bool get allPublicInstanceMethodsInherited =>
instanceMethods.every((f) => f.isInherited);
+ @override
Iterable<Field> get allInstanceFields =>
quiver.concat([instanceProperties, inheritedProperties]);
- Iterable<Accessor> get allAccessors => quiver.concat([
- allInstanceFields.expand((f) => f.allAccessors),
- constants.map((c) => c.getter)
- ]);
-
- Iterable<Field> get allPublicInstanceProperties =>
- filterNonPublic(allInstanceFields);
-
bool get allPublicInstancePropertiesInherited =>
allPublicInstanceProperties.every((f) => f.isInherited);
+ @override
Iterable<Operator> get allOperators =>
quiver.concat([operators, inheritedOperators]);
- Iterable<Operator> get allPublicOperators => filterNonPublic(allOperators);
-
bool get allPublicOperatorsInherited =>
allPublicOperators.every((f) => f.isInherited);
- List<Field> get constants {
- if (_constants != null) return _constants;
- _constants = _allFields.where((f) => f.isConst).toList(growable: false)
- ..sort(byName);
-
- return _constants;
- }
-
- Iterable<Field> get publicConstants => filterNonPublic(constants);
-
Map<Element, ModelElement> _allElements;
Map<Element, ModelElement> get allElements {
@@ -786,18 +900,15 @@
return kind;
}
- bool get hasPublicConstants => publicConstants.isNotEmpty;
-
bool get hasPublicConstructors => publicConstructors.isNotEmpty;
bool get hasPublicImplementors => publicImplementors.isNotEmpty;
- bool get hasInstanceMethods => instanceMethods.isNotEmpty;
-
bool get hasInstanceProperties => instanceProperties.isNotEmpty;
bool get hasPublicInterfaces => publicInterfaces.isNotEmpty;
+ @override
bool get hasPublicMethods =>
publicInstanceMethods.isNotEmpty || publicInheritedMethods.isNotEmpty;
@@ -810,17 +921,18 @@
hasPublicSuperChainReversed ||
hasPublicImplementors;
+ @override
bool get hasPublicOperators =>
publicOperators.isNotEmpty || publicInheritedOperators.isNotEmpty;
+ @override
bool get hasPublicProperties =>
publicInheritedProperties.isNotEmpty ||
publicInstanceProperties.isNotEmpty;
+ @override
bool get hasPublicStaticMethods => publicStaticMethods.isNotEmpty;
- bool get hasPublicStaticProperties => publicStaticProperties.isNotEmpty;
-
bool get hasPublicSuperChainReversed => publicSuperChainReversed.isNotEmpty;
@override
@@ -903,31 +1015,8 @@
Iterable<Field> get publicInheritedProperties =>
filterNonPublic(inheritedProperties);
- List<Method> get instanceMethods {
- if (_instanceMethods != null) return _instanceMethods;
-
- _instanceMethods = _methods
- .where((m) => !m.isStatic && !m.isOperator)
- .toList(growable: false)
- ..sort(byName);
- return _instanceMethods;
- }
-
Iterable<Method> get publicInstanceMethods => instanceMethods;
- List<Field> get instanceProperties {
- if (_instanceFields != null) return _instanceFields;
- _instanceFields = _allFields
- .where((f) => !f.isStatic && !f.isInherited && !f.isConst)
- .toList(growable: false)
- ..sort(byName);
-
- return _instanceFields;
- }
-
- Iterable<Field> get publicInstanceProperties =>
- filterNonPublic(instanceProperties);
-
List<DefinedElementType> get interfaces => _interfaces;
Iterable<DefinedElementType> get publicInterfaces =>
@@ -951,7 +1040,8 @@
}
/// Returns true if [other] is a parent class for this class.
- bool isInheritingFrom(Class other) =>
+ @override
+ bool isInheritingFrom(covariant Class other) =>
superChain.map((et) => (et.element as Class)).contains(other);
@override
@@ -964,42 +1054,6 @@
@override
DefinedElementType get modelType => super.modelType;
- List<Operator> get operators {
- if (_operators != null) return _operators;
- _operators = _methods
- .where((m) => m.isOperator)
- .cast<Operator>()
- .toList(growable: false)
- ..sort(byName);
- return _operators;
- }
-
- Iterable<Operator> get publicOperators => filterNonPublic(operators);
-
- List<Method> get staticMethods {
- if (_staticMethods != null) return _staticMethods;
-
- _staticMethods = _methods.where((m) => m.isStatic).toList(growable: false)
- ..sort(byName);
-
- return _staticMethods;
- }
-
- Iterable<Method> get publicStaticMethods => filterNonPublic(staticMethods);
-
- List<Field> get staticProperties {
- if (_staticFields != null) return _staticFields;
- _staticFields = _allFields
- .where((f) => f.isStatic && !f.isConst)
- .toList(growable: false)
- ..sort(byName);
-
- return _staticFields;
- }
-
- Iterable<Field> get publicStaticProperties =>
- filterNonPublic(staticProperties);
-
/// Not the same as superChain as it may include mixins.
/// It's really not even the same as ordinary Dart inheritance, either,
/// because we pretend that interfaces are part of the inheritance chain
@@ -1074,6 +1128,7 @@
/// Internal only because subclasses are allowed to override how
/// these are mapped to [allInheritedFields] and so forth.
+ @override
List<Field> get _allFields {
if (_fields != null) return _fields;
_fields = [];
@@ -1181,6 +1236,7 @@
ClassElement get _cls => (element as ClassElement);
+ @override
List<Method> get _methods {
if (_allMethods != null) return _allMethods;
@@ -1192,8 +1248,6 @@
return _allMethods;
}
- List<TypeParameter> _typeParameters;
-
// a stronger hash?
@override
List<TypeParameter> get typeParameters {
@@ -1214,6 +1268,104 @@
o.library.package.name == library.package.name;
}
+/// Extension methods
+class Extension extends Container
+ with TypeParameters, Categorization
+ implements EnclosedElement {
+ DefinedElementType extendedType;
+
+ Extension(
+ ExtensionElement element, Library library, PackageGraph packageGraph)
+ : super(element, library, packageGraph) {
+ extendedType = ElementType.from(_extension.extendedType, packageGraph);
+ }
+
+ @override
+ ModelElement get enclosingElement => library;
+
+ ExtensionElement get _extension => (element as ExtensionElement);
+
+ @override
+ String get kind => 'extension';
+
+ @override
+ List<Method> get _methods {
+ if (_allMethods != null) return _allMethods;
+
+ _allMethods = _extension.methods.map((e) {
+ return ModelElement.from(e, library, packageGraph) as Method;
+ }).toList(growable: false)
+ ..sort(byName);
+
+ return _allMethods;
+ }
+
+ @override
+ List<Field> get _allFields {
+ if (_fields != null) return _fields;
+ _fields = _extension.fields.map((f) {
+ Accessor getter, setter;
+ if (f.getter != null) {
+ getter = InheritableAccessor(f.getter, library, packageGraph);
+ }
+ if (f.setter != null) {
+ setter = InheritableAccessor(f.setter, library, packageGraph);
+ }
+
+ return ModelElement.from(f, library, packageGraph,
+ enclosingClass: this, getter: getter, setter: setter) as Field;
+ }).toList(growable: false)
+ ..sort(byName);
+
+ return _fields;
+ }
+
+ // a stronger hash?
+ @override
+ List<TypeParameter> get typeParameters {
+ if (_typeParameters == null) {
+ _typeParameters = _extension.typeParameters.map((f) {
+ var lib = Library(f.enclosingElement.library, packageGraph);
+ return ModelElement.from(f, lib, packageGraph) as TypeParameter;
+ }).toList();
+ }
+ return _typeParameters;
+ }
+
+ @override
+ DefinedElementType get modelType => super.modelType;
+
+ List<ModelElement> _allModelElements;
+
+ List<ModelElement> get allModelElements {
+ if (_allModelElements == null) {
+ _allModelElements = List.from(
+ quiver.concat([
+ instanceMethods,
+ allInstanceFields,
+ allAccessors,
+ allOperators,
+ constants,
+ staticMethods,
+ staticProperties,
+ typeParameters,
+ ]),
+ growable: false);
+ }
+ return _allModelElements;
+ }
+
+ @override
+ String get href {
+ if (!identical(canonicalModelElement, this)) {
+ return canonicalModelElement?.href;
+ }
+ assert(canonicalLibrary != null);
+ assert(canonicalLibrary == library);
+ return '${package.baseHref}${library.dirName}/$fileName';
+ }
+}
+
class Constructor extends ModelElement
with TypeParameters
implements EnclosedElement {
@@ -1711,7 +1863,7 @@
with GetterSetterCombo, Inheritable
implements EnclosedElement {
bool _isInherited = false;
- Class _enclosingClass;
+ Container _enclosingClass;
@override
final InheritableAccessor getter;
@override
@@ -2205,6 +2357,16 @@
.toList(growable: false);
}
+ @override
+ Iterable<Extension> get extensions {
+ if (_extensions != null) return _extensions;
+ _extensions = _libraryElement.definingCompilationUnit.extensions
+ .map((e) => ModelElement.from(e, this, packageGraph) as Extension)
+ .toList(growable: false)
+ ..sort(byName);
+ return _extensions;
+ }
+
SdkLibrary get sdkLib {
if (packageGraph.sdkLibrarySources.containsKey(element.librarySource)) {
return packageGraph.sdkLibrarySources[element.librarySource];
@@ -2661,6 +2823,12 @@
library.functions,
library.properties,
library.typedefs,
+ library.extensions.expand((e) {
+ return quiver.concat([
+ [e],
+ e.allModelElements
+ ]);
+ }),
library.allClasses.expand((c) {
return quiver.concat([
[c],
@@ -2715,7 +2883,7 @@
with Inheritable, TypeParameters
implements EnclosedElement {
bool _isInherited = false;
- Class _enclosingClass;
+ Container _enclosingClass;
@override
List<TypeParameter> typeParameters = [];
@@ -2788,6 +2956,9 @@
@override
Method get overriddenElement {
+ if (_enclosingClass is Extension) {
+ return null;
+ }
ClassElement parent = element.enclosingElement;
for (InterfaceType t in parent.allSupertypes) {
Element e = t.getMethod(element.name);
@@ -2941,7 +3112,7 @@
/// Specify enclosingClass only if this is to be an inherited object.
factory ModelElement.from(
Element e, Library library, PackageGraph packageGraph,
- {Class enclosingClass, Accessor getter, Accessor setter}) {
+ {Container enclosingClass, Accessor getter, Accessor setter}) {
assert(packageGraph != null && e != null);
assert(library != null ||
e is ParameterElement ||
@@ -2962,7 +3133,8 @@
originalMember = e;
e = basest;
}
- Tuple3<Element, Library, Class> key = Tuple3(e, library, enclosingClass);
+ Tuple3<Element, Library, Container> key =
+ Tuple3(e, library, enclosingClass);
ModelElement newModelElement;
if (e.kind != ElementKind.DYNAMIC &&
packageGraph._allConstructedModelElements.containsKey(key)) {
@@ -2989,6 +3161,9 @@
newModelElement = Class(e, library, packageGraph);
}
}
+ if (e is ExtensionElement) {
+ newModelElement = Extension(e, library, packageGraph);
+ }
if (e is FunctionElement) {
newModelElement = ModelFunction(e, library, packageGraph);
} else if (e is GenericFunctionTypeElement) {
@@ -3018,16 +3193,23 @@
newModelElement = EnumField.forConstant(
index, e, library, packageGraph, getter);
// ignore: unnecessary_cast
- } else if ((e.enclosingElement as ClassElement).isEnum) {
+ } else if (e.enclosingElement is ExtensionElement) {
+ newModelElement = Field(e, library, packageGraph, getter, setter);
+ } else if (e.enclosingElement is ClassElement &&
+ (e.enclosingElement as ClassElement).isEnum) {
newModelElement =
EnumField(e, library, packageGraph, getter, setter);
} else {
newModelElement = Field(e, library, packageGraph, getter, setter);
}
} else {
- // EnumFields can't be inherited, so this case is simpler.
- newModelElement = Field.inherited(
- e, enclosingClass, library, packageGraph, getter, setter);
+ if (enclosingClass is Extension) {
+ newModelElement = Field(e, library, packageGraph, getter, setter);
+ } else {
+ // EnumFields can't be inherited, so this case is simpler.
+ newModelElement = Field.inherited(
+ e, enclosingClass, library, packageGraph, getter, setter);
+ }
}
}
if (e is ConstructorElement) {
@@ -3173,6 +3355,9 @@
} else if (enclosingElement is Class &&
!(enclosingElement as Class).isPublic) {
_isPublic = false;
+ } else if (enclosingElement is Extension &&
+ !(enclosingElement as Extension).isPublic) {
+ _isPublic = false;
} else {
String docComment = documentationComment;
if (docComment == null) {
@@ -3251,8 +3436,8 @@
element is ExecutableElement || element is FunctionTypedElement;
ModelElement _buildCanonicalModelElement() {
- Class preferredClass;
- if (enclosingElement is Class) {
+ Container preferredClass;
+ if (enclosingElement is Class || enclosingElement is Extension) {
preferredClass = enclosingElement;
}
return packageGraph.findCanonicalModelElementFor(element,
@@ -4963,7 +5148,7 @@
PackageWarningCounter _packageWarningCounter;
/// All ModelElements constructed for this package; a superset of [allModelElements].
- final Map<Tuple3<Element, Library, Class>, ModelElement>
+ final Map<Tuple3<Element, Library, Container>, ModelElement>
_allConstructedModelElements = Map();
/// Anything that might be inheritable, place here for later lookup.
@@ -5468,11 +5653,12 @@
/// This doesn't know anything about [PackageGraph.inheritThrough] and probably
/// shouldn't, so using it with [Inheritable]s without special casing is
/// not advised.
- ModelElement findCanonicalModelElementFor(Element e, {Class preferredClass}) {
+ ModelElement findCanonicalModelElementFor(Element e,
+ {Container preferredClass}) {
assert(allLibrariesAdded);
Library lib = findCanonicalLibraryFor(e);
- if (preferredClass != null) {
- Class canonicalClass =
+ if (preferredClass != null && preferredClass is Container) {
+ Container canonicalClass =
findCanonicalModelElementFor(preferredClass.element);
if (canonicalClass != null) preferredClass = canonicalClass;
}
@@ -5480,6 +5666,15 @@
lib = findCanonicalLibraryFor(preferredClass.element);
}
ModelElement modelElement;
+ // For elements defined in extensions, they are canonical.
+ if (e?.enclosingElement is ExtensionElement) {
+ lib ??= Library(e.enclosingElement.library, packageGraph);
+ // (TODO:keertip) Find a better way to exclude members of extensions
+ // when libraries are specified using the "--include" flag
+ if (lib?.isDocumented) {
+ return ModelElement.from(e, lib, packageGraph);
+ }
+ }
// TODO(jcollins-g): Special cases are pretty large here. Refactor to split
// out into helpers.
// TODO(jcollins-g): The data structures should be changed to eliminate guesswork
@@ -5523,7 +5718,9 @@
// This is for situations where multiple classes may actually be canonical
// for an inherited element whose defining Class is not canonical.
- if (matches.length > 1 && preferredClass != null) {
+ if (matches.length > 1 &&
+ preferredClass != null &&
+ preferredClass is Class) {
// Search for matches inside our superchain.
List<Class> superChain = preferredClass.superChain
.map((et) => et.element)
@@ -5692,6 +5889,7 @@
/// of a [TopLevelContainer].
abstract class TopLevelContainer implements Nameable {
List<Class> _classes;
+ List<Extension> _extensions;
List<Enum> _enums;
List<Mixin> _mixins;
List<Class> _exceptions;
@@ -5702,6 +5900,8 @@
Iterable<Class> get classes => _classes;
+ Iterable<Extension> get extensions => _extensions;
+
Iterable<Enum> get enums => _enums;
Iterable<Mixin> get mixins => _mixins;
@@ -5718,6 +5918,8 @@
bool get hasPublicClasses => publicClasses.isNotEmpty;
+ bool get hasPublicExtensions => publicExtensions.isNotEmpty;
+
bool get hasPublicConstants => publicConstants.isNotEmpty;
bool get hasPublicEnums => publicEnums.isNotEmpty;
@@ -5734,6 +5936,8 @@
Iterable<Class> get publicClasses => filterNonPublic(classes);
+ Iterable<Extension> get publicExtensions => filterNonPublic(extensions);
+
Iterable<TopLevelVariable> get publicConstants => filterNonPublic(constants);
Iterable<Enum> get publicEnums => filterNonPublic(enums);
@@ -5876,6 +6080,7 @@
_functions = [];
_mixins = [];
_typedefs = [];
+ _extensions = [];
}
void addItem(Categorization c) {
@@ -5903,6 +6108,8 @@
_functions.add(c);
} else if (c is Typedef) {
_typedefs.add(c);
+ } else if (c is Extension) {
+ _extensions.add(c);
} else {
throw UnimplementedError("Unrecognized element");
}
@@ -6739,7 +6946,8 @@
AnalysisOptionsImpl options = AnalysisOptionsImpl();
// TODO(jcollins-g): pass in an ExperimentStatus instead?
- options.enabledExperiments = config.enableExperiment;
+ options.enabledExperiments = config.enableExperiment
+ ..add('extension-methods');
// TODO(jcollins-g): Make use of currently not existing API for managing
// many AnalysisDrivers
diff --git a/lib/src/version.dart b/lib/src/version.dart
index 6d46787..d25679d 100644
--- a/lib/src/version.dart
+++ b/lib/src/version.dart
@@ -1,2 +1,2 @@
// Generated code. Do not modify.
-const packageVersion = '0.28.5';
+const packageVersion = '0.28.6-dev';
diff --git a/lib/templates/_extension.html b/lib/templates/_extension.html
new file mode 100644
index 0000000..d35e458
--- /dev/null
+++ b/lib/templates/_extension.html
@@ -0,0 +1,7 @@
+<dt id="{{htmlId}}">
+ <span class="name {{#isDeprecated}}deprecated{{/isDeprecated}}">{{{linkedName}}}</span> {{>categorization}}
+</dt>
+<dd>
+ {{{ oneLineDoc }}}
+</dd>
+
diff --git a/lib/templates/_sidebar_for_class.html b/lib/templates/_sidebar_for_class.html
index 3a0880e..d0b1bb0 100644
--- a/lib/templates/_sidebar_for_class.html
+++ b/lib/templates/_sidebar_for_class.html
@@ -1,5 +1,7 @@
<ol>
{{#clazz}}
+
+ {{#isClass}}
{{#hasPublicConstructors}}
<li class="section-title"><a href="{{{href}}}#constructors">Constructors</a></li>
{{#publicConstructors}}
@@ -50,5 +52,52 @@
<li>{{{linkedName}}}</li>
{{/publicConstants}}
{{/hasPublicConstants}}
+ {{/isClass}}
+
+ {{#isExtension}}
+ {{#hasPublicProperties}}
+ <li class="section-title"> <a href="{{{href}}}#instance-properties">Properties</a>
+ </li>
+ {{#allPublicInstanceProperties}}
+ <li>{{{ linkedName }}}</li>
+ {{/allPublicInstanceProperties}}
+ {{/hasPublicProperties}}
+
+ {{#hasPublicMethods}}
+ <li class="section-title"><a href="{{{href}}}#instance-methods">Methods</a></li>
+ {{#allPublicInstanceMethods}}
+ <li>{{{ linkedName }}}</li>
+ {{/allPublicInstanceMethods}}
+ {{/hasPublicMethods}}
+
+ {{#hasPublicOperators}}
+ <li class="section-title"><a href="{{{href}}}#operators">Operators</a></li>
+ {{#allPublicOperators}}
+ <li>{{{ linkedName }}}</li>
+ {{/allPublicOperators}}
+ {{/hasPublicOperators}}
+
+ {{#hasPublicStaticProperties}}
+ <li class="section-title"><a href="{{{href}}}#static-properties">Static properties</a></li>
+ {{#publicStaticProperties}}
+ <li>{{{ linkedName }}}</li>
+ {{/publicStaticProperties}}
+ {{/hasPublicStaticProperties}}
+
+ {{#hasPublicStaticMethods}}
+ <li class="section-title"><a href="{{{href}}}#static-methods">Static methods</a></li>
+ {{#publicStaticMethods}}
+ <li>{{{ linkedName }}}</li>
+ {{/publicStaticMethods}}
+ {{/hasPublicStaticMethods}}
+
+ {{#hasPublicConstants}}
+ <li class="section-title"><a href="{{{href}}}#constants">Constants</a></li>
+ {{#publicConstants}}
+ <li>{{{linkedName}}}</li>
+ {{/publicConstants}}
+ {{/hasPublicConstants}}
+ {{/isExtension}}
+
{{/clazz}}
</ol>
diff --git a/lib/templates/_sidebar_for_extension.html b/lib/templates/_sidebar_for_extension.html
new file mode 100644
index 0000000..138db39
--- /dev/null
+++ b/lib/templates/_sidebar_for_extension.html
@@ -0,0 +1,48 @@
+<ol>
+ {{#extension}}
+
+ {{#hasPublicProperties}}
+ <li class="section-title"> <a href="{{{href}}}#instance-properties">Properties</a>
+ </li>
+ {{#allPublicInstanceProperties}}
+ <li>{{{ linkedName }}}</li>
+ {{/allPublicInstanceProperties}}
+ {{/hasPublicProperties}}
+
+ {{#hasPublicMethods}}
+ <li class="section-title"><a href="{{{href}}}#instance-methods">Methods</a></li>
+ {{#allPublicInstanceMethods}}
+ <li>{{{ linkedName }}}</li>
+ {{/allPublicInstanceMethods}}
+ {{/hasPublicMethods}}
+
+ {{#hasPublicOperators}}
+ <li class="section-title"><a href="{{{href}}}#operators">Operators</a></li>
+ {{#allPublicOperators}}
+ <li>{{{ linkedName }}}</li>
+ {{/allPublicOperators}}
+ {{/hasPublicOperators}}
+
+ {{#hasPublicStaticProperties}}
+ <li class="section-title"><a href="{{{href}}}#static-properties">Static properties</a></li>
+ {{#publicStaticProperties}}
+ <li>{{{ linkedName }}}</li>
+ {{/publicStaticProperties}}
+ {{/hasPublicStaticProperties}}
+
+ {{#hasPublicStaticMethods}}
+ <li class="section-title"><a href="{{{href}}}#static-methods">Static methods</a></li>
+ {{#publicStaticMethods}}
+ <li>{{{ linkedName }}}</li>
+ {{/publicStaticMethods}}
+ {{/hasPublicStaticMethods}}
+
+ {{#hasPublicConstants}}
+ <li class="section-title"><a href="{{{href}}}#constants">Constants</a></li>
+ {{#publicConstants}}
+ <li>{{{linkedName}}}</li>
+ {{/publicConstants}}
+ {{/hasPublicConstants}}
+
+ {{/extension}}
+</ol>
diff --git a/lib/templates/_sidebar_for_library.html b/lib/templates/_sidebar_for_library.html
index 43dfc8a..726b309 100644
--- a/lib/templates/_sidebar_for_library.html
+++ b/lib/templates/_sidebar_for_library.html
@@ -7,6 +7,13 @@
{{/publicClasses}}
{{/hasPublicClasses}}
+ {{#hasPublicExtensions}}
+ <li class="section-title"><a href="{{{href}}}#extension">Extensions</a></li>
+ {{#publicExtensions}}
+ <li>{{{ linkedName }}}</li>
+ {{/publicExtensions}}
+ {{/hasPublicExtensions}}
+
{{#hasPublicMixins}}
<li class="section-title"><a href="{{{href}}}#mixins">Mixins</a></li>
{{#publicMixins}}
diff --git a/lib/templates/extension.html b/lib/templates/extension.html
new file mode 100644
index 0000000..9fdee1c
--- /dev/null
+++ b/lib/templates/extension.html
@@ -0,0 +1,103 @@
+{{>head}}
+
+<div id="dartdoc-sidebar-left" class="col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left">
+ {{>search_sidebar}}
+ <h5>{{parent.name}} {{parent.kind}}</h5>
+ {{>sidebar_for_library}}
+</div>
+
+<div id="dartdoc-main-content" class="col-xs-12 col-sm-9 col-md-8 main-content">
+ {{#self}}
+ <div>{{>source_link}}<h1><span class="kind-class">{{{nameWithGenerics}}}</span> {{kind}} {{>categorization}}</h1></div>
+ {{/self}}
+
+ {{#extension}}
+ {{>documentation}}
+
+ <dt>on</dt>
+ <dd>
+ <ul class="comma-separated clazz-relationships">
+ {{#extendedType}}
+ <li>{{{linkedName}}}</li>
+ {{/extendedType}}
+ </ul>
+ </dd>
+
+
+ {{#hasPublicProperties}}
+ <section class="summary offset-anchor" id="instance-properties">
+ <h2>Properties</h2>
+
+ <dl class="properties">
+ {{#allPublicInstanceProperties}}
+ {{>property}}
+ {{/allPublicInstanceProperties}}
+ </dl>
+ </section>
+ {{/hasPublicProperties}}
+
+ {{#hasPublicMethods}}
+ <section class="summary offset-anchor" id="instance-methods">
+ <h2>Methods</h2>
+ <dl class="callables">
+ {{#allPublicInstanceMethods}}
+ {{>callable}}
+ {{/allPublicInstanceMethods}}
+ </dl>
+ </section>
+ {{/hasPublicMethods}}
+
+ {{#hasPublicOperators}}
+ <section class="summary offset-anchor" id="operators">
+ <h2>Operators</h2>
+ <dl class="callables">
+ {{#allPublicOperators}}
+ {{>callable}}
+ {{/allPublicOperators}}
+ </dl>
+ </section>
+ {{/hasPublicOperators}}
+
+ {{#hasPublicStaticProperties}}
+ <section class="summary offset-anchor" id="static-properties">
+ <h2>Static Properties</h2>
+
+ <dl class="properties">
+ {{#publicStaticProperties}}
+ {{>property}}
+ {{/publicStaticProperties}}
+ </dl>
+ </section>
+ {{/hasPublicStaticProperties}}
+
+ {{#hasPublicStaticMethods}}
+ <section class="summary offset-anchor" id="static-methods">
+ <h2>Static Methods</h2>
+ <dl class="callables">
+ {{#publicStaticMethods}}
+ {{>callable}}
+ {{/publicStaticMethods}}
+ </dl>
+ </section>
+ {{/hasPublicStaticMethods}}
+
+ {{#hasPublicConstants}}
+ <section class="summary offset-anchor" id="constants">
+ <h2>Constants</h2>
+
+ <dl class="properties">
+ {{#publicConstants}}
+ {{>constant}}
+ {{/publicConstants}}
+ </dl>
+ </section>
+ {{/hasPublicConstants}}
+ {{/extension}}
+
+</div> <!-- /.main-content -->
+
+<div id="dartdoc-sidebar-right" class="col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right">
+ {{>sidebar_for_extension}}
+</div><!--/.sidebar-offcanvas-->
+
+{{>footer}}
diff --git a/lib/templates/library.html b/lib/templates/library.html
index 1aa24b2..6e08376 100644
--- a/lib/templates/library.html
+++ b/lib/templates/library.html
@@ -39,6 +39,18 @@
</section>
{{/library.hasPublicMixins}}
+ {{#library.hasPublicExtensions}}
+ <section class="summary offset-anchor" id="extensions">
+ <h2>Extensions</h2>
+
+ <dl>
+ {{#library.publicExtensions}}
+ {{>extension}}
+ {{/library.publicExtensions}}
+ </dl>
+ </section>
+ {{/library.hasPublicExtensions}}
+
{{#library.hasPublicConstants}}
<section class="summary offset-anchor" id="constants">
<h2>Constants</h2>
diff --git a/pubspec.yaml b/pubspec.yaml
index e11d8cb..fa96ecb 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,6 +1,6 @@
name: dartdoc
# Run `grind build` after updating.
-version: 0.28.5
+version: 0.28.6-dev
author: Dart Team <misc@dartlang.org>
description: A documentation generator for Dart.
homepage: https://github.com/dart-lang/dartdoc
@@ -8,7 +8,7 @@
sdk: '>=2.1.0 <3.0.0'
dependencies:
- analyzer: '>=0.36.3 <0.38.0'
+ analyzer: ^0.38.2
args: '>=1.4.1 <2.0.0'
collection: ^1.2.0
crypto: ^2.0.6
diff --git a/test/dartdoc_test.dart b/test/dartdoc_test.dart
index 66bdbde..c47ade0 100644
--- a/test/dartdoc_test.dart
+++ b/test/dartdoc_test.dart
@@ -365,10 +365,10 @@
});
test('generate docs with custom templates', () async {
- String templatesDir = path.join(testPackageCustomTemplates.path, 'templates');
- Dartdoc dartdoc =
- await buildDartdoc(['--templates-dir', templatesDir],
- testPackageCustomTemplates, tempDir);
+ String templatesDir =
+ path.join(testPackageCustomTemplates.path, 'templates');
+ Dartdoc dartdoc = await buildDartdoc(['--templates-dir', templatesDir],
+ testPackageCustomTemplates, tempDir);
DartdocResults results = await dartdoc.generateDocs();
expect(results.packageGraph, isNotNull);
@@ -381,18 +381,21 @@
test('generate docs with missing required template fails', () async {
var templatesDir = path.join(path.current, 'test/templates');
try {
- await buildDartdoc(['--templates-dir', templatesDir], testPackageCustomTemplates, tempDir);
+ await buildDartdoc(['--templates-dir', templatesDir],
+ testPackageCustomTemplates, tempDir);
fail('dartdoc should fail with missing required template');
} catch (e) {
expect(e is DartdocFailure, isTrue);
- expect((e as DartdocFailure).message, startsWith('Missing required template file'));
+ expect((e as DartdocFailure).message,
+ startsWith('Missing required template file'));
}
});
test('generate docs with bad templatesDir path fails', () async {
String badPath = path.join(tempDir.path, 'BAD');
try {
- await buildDartdoc(['--templates-dir', badPath], testPackageCustomTemplates, tempDir);
+ await buildDartdoc(
+ ['--templates-dir', badPath], testPackageCustomTemplates, tempDir);
fail('dartdoc should fail with bad templatesDir path');
} catch (e) {
expect(e is DartdocFailure, isTrue);
diff --git a/test/model_test.dart b/test/model_test.dart
index 1ef9067..031618f 100644
--- a/test/model_test.dart
+++ b/test/model_test.dart
@@ -2099,6 +2099,71 @@
});
});
+ group('Extension', () {
+ Extension ext, anExt, fancyList;
+ Method s;
+ List<Extension> extensions;
+
+ setUpAll(() {
+ ext = exLibrary.extensions.firstWhere((e) => e.name == 'AppleExtension');
+ anExt = exLibrary.extensions.firstWhere((e) => e.name == 'AnExtension');
+ fancyList = exLibrary.extensions.firstWhere((e) => e.name == 'FancyList');
+ extensions = exLibrary.publicExtensions.toList();
+ });
+
+ test('has a fully qualified name', () {
+ expect(ext.fullyQualifiedName, 'ex.AppleExtension');
+ });
+
+ test('has enclosing element', () {
+ expect(ext.enclosingElement.name, equals(exLibrary.name));
+ });
+
+ test('member method has href', () {
+ s = ext.instanceMethods.firstWhere((m) => m.name == 's');
+ expect(s.href, 'ex/AppleExtension/s.html');
+ });
+
+ test('has extended type', () {
+ expect(ext.extendedType.name, equals("Apple"));
+ });
+
+ test('extension name with generics', () {
+ expect(
+ fancyList.nameWithGenerics,
+ equals(
+ 'FancyList<<wbr><span class="type-parameter">Z</span>>'));
+ });
+
+ test('get methods', () {
+ expect(fancyList.allPublicInstanceMethods, hasLength(1));
+ });
+
+ test('get operators', () {
+ expect(fancyList.allPublicOperators, hasLength(1));
+ });
+
+ test('get static methods', () {
+ expect(fancyList.publicStaticMethods, hasLength(1));
+ });
+
+ test('get properties', () {
+ expect(fancyList.publicInstanceProperties, hasLength(1));
+ });
+
+ test('get contants', () {
+ expect(fancyList.publicConstants, hasLength(0));
+ });
+
+ test('correctly finds all the extensions', () {
+ expect(exLibrary.extensions, hasLength(7));
+ });
+
+ test('correctly finds all the public extensions', () {
+ expect(extensions, hasLength(5));
+ });
+ });
+
group('Enum', () {
Enum animal;
diff --git a/testing/test_package/.analysis_options b/testing/test_package/.analysis_options
deleted file mode 100644
index 234da62..0000000
--- a/testing/test_package/.analysis_options
+++ /dev/null
@@ -1 +0,0 @@
-{ analyzer: { exclude: ['**'] } }
diff --git a/testing/test_package/analysis_options.yaml b/testing/test_package/analysis_options.yaml
new file mode 100644
index 0000000..8b7c84a
--- /dev/null
+++ b/testing/test_package/analysis_options.yaml
@@ -0,0 +1,6 @@
+analyzer:
+ enable-experiment:
+ - extension-methods
+ exclude: ['**']
+
+
diff --git a/testing/test_package/lib/example.dart b/testing/test_package/lib/example.dart
index b464043..4cba5f5 100644
--- a/testing/test_package/lib/example.dart
+++ b/testing/test_package/lib/example.dart
@@ -200,6 +200,16 @@
final ParameterizedTypedef<bool> fieldWithTypedef;
}
+
+/// Extension on Apple
+extension AppleExtension on Apple {
+/// Can call s on Apple
+ void s() {
+ print('Extension on Apple');
+ }
+}
+
+
class WithGeneric<T> {
T prop;
WithGeneric(this.prop);
@@ -608,3 +618,37 @@
/// {@end-tool}
void injectHtmlFromTool();
}
+
+/// Extension on a class defined in the package
+extension AnExtension<Q> on WithGeneric<Q> {
+ int call(String s) => 0;
+}
+
+/// Extension on List
+extension FancyList<Z> on List<Z> {
+ int get doubleLength => this.length * 2;
+ List<Z> operator-() => this.reversed.toList();
+ List<List<Z>> split(int at) =>
+ <List<Z>>[this.sublist(0, at), this.sublist(at)];
+ static List<Z> big() => List(1000000);
+}
+
+extension SymDiff<Q> on Set<Q> {
+ Set<Q> symmetricDifference(Set<Q> other) =>
+ this.difference(other).union(other.difference(this));
+}
+
+/// Extensions can be made specific.
+extension IntSet on Set<int> {
+ int sum() => this.fold(0, (prev, element) => prev + element);
+}
+
+// Extensions can be private.
+extension _Shhh on Object {
+ void secret() { }
+}
+
+// Extension with no name
+extension on Object {
+ void bar() { }
+}
\ No newline at end of file
diff --git a/testing/test_package_custom_templates/templates/extension.html b/testing/test_package_custom_templates/templates/extension.html
new file mode 100644
index 0000000..9fdee1c
--- /dev/null
+++ b/testing/test_package_custom_templates/templates/extension.html
@@ -0,0 +1,103 @@
+{{>head}}
+
+<div id="dartdoc-sidebar-left" class="col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left">
+ {{>search_sidebar}}
+ <h5>{{parent.name}} {{parent.kind}}</h5>
+ {{>sidebar_for_library}}
+</div>
+
+<div id="dartdoc-main-content" class="col-xs-12 col-sm-9 col-md-8 main-content">
+ {{#self}}
+ <div>{{>source_link}}<h1><span class="kind-class">{{{nameWithGenerics}}}</span> {{kind}} {{>categorization}}</h1></div>
+ {{/self}}
+
+ {{#extension}}
+ {{>documentation}}
+
+ <dt>on</dt>
+ <dd>
+ <ul class="comma-separated clazz-relationships">
+ {{#extendedType}}
+ <li>{{{linkedName}}}</li>
+ {{/extendedType}}
+ </ul>
+ </dd>
+
+
+ {{#hasPublicProperties}}
+ <section class="summary offset-anchor" id="instance-properties">
+ <h2>Properties</h2>
+
+ <dl class="properties">
+ {{#allPublicInstanceProperties}}
+ {{>property}}
+ {{/allPublicInstanceProperties}}
+ </dl>
+ </section>
+ {{/hasPublicProperties}}
+
+ {{#hasPublicMethods}}
+ <section class="summary offset-anchor" id="instance-methods">
+ <h2>Methods</h2>
+ <dl class="callables">
+ {{#allPublicInstanceMethods}}
+ {{>callable}}
+ {{/allPublicInstanceMethods}}
+ </dl>
+ </section>
+ {{/hasPublicMethods}}
+
+ {{#hasPublicOperators}}
+ <section class="summary offset-anchor" id="operators">
+ <h2>Operators</h2>
+ <dl class="callables">
+ {{#allPublicOperators}}
+ {{>callable}}
+ {{/allPublicOperators}}
+ </dl>
+ </section>
+ {{/hasPublicOperators}}
+
+ {{#hasPublicStaticProperties}}
+ <section class="summary offset-anchor" id="static-properties">
+ <h2>Static Properties</h2>
+
+ <dl class="properties">
+ {{#publicStaticProperties}}
+ {{>property}}
+ {{/publicStaticProperties}}
+ </dl>
+ </section>
+ {{/hasPublicStaticProperties}}
+
+ {{#hasPublicStaticMethods}}
+ <section class="summary offset-anchor" id="static-methods">
+ <h2>Static Methods</h2>
+ <dl class="callables">
+ {{#publicStaticMethods}}
+ {{>callable}}
+ {{/publicStaticMethods}}
+ </dl>
+ </section>
+ {{/hasPublicStaticMethods}}
+
+ {{#hasPublicConstants}}
+ <section class="summary offset-anchor" id="constants">
+ <h2>Constants</h2>
+
+ <dl class="properties">
+ {{#publicConstants}}
+ {{>constant}}
+ {{/publicConstants}}
+ </dl>
+ </section>
+ {{/hasPublicConstants}}
+ {{/extension}}
+
+</div> <!-- /.main-content -->
+
+<div id="dartdoc-sidebar-right" class="col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right">
+ {{>sidebar_for_extension}}
+</div><!--/.sidebar-offcanvas-->
+
+{{>footer}}
diff --git a/testing/test_package_extensions/analysis_options.yaml b/testing/test_package_extensions/analysis_options.yaml
new file mode 100644
index 0000000..8b7c84a
--- /dev/null
+++ b/testing/test_package_extensions/analysis_options.yaml
@@ -0,0 +1,6 @@
+analyzer:
+ enable-experiment:
+ - extension-methods
+ exclude: ['**']
+
+
diff --git a/testing/test_package_extensions/lib/main.dart b/testing/test_package_extensions/lib/main.dart
new file mode 100644
index 0000000..16c6684
--- /dev/null
+++ b/testing/test_package_extensions/lib/main.dart
@@ -0,0 +1,52 @@
+/// library with extensions
+library ext;
+
+class Apple<M> {
+ final String name = 'name';
+
+ bool get isImplemented => true;
+}
+
+class _Private {
+ void sIs() {}
+}
+
+/// Extension on a class defined in the package
+extension AnExtension on Apple {
+ int call(String s) => 0;
+}
+
+/// Extension on List
+extension FancyList<Z> on List<Z> {
+ int get doubleLength => this.length * 2;
+ List<Z> operator-() => this.reversed.toList();
+ List<List<Z>> split(int at) =>
+ <List<Z>>[this.sublist(0, at), this.sublist(at)];
+ static List<Z> big() => List(1000000);
+}
+
+extension SymDiff<T> on Set<T> {
+ Set<T> symmetricDifference(Set<T> other) =>
+ this.difference(other).union(other.difference(this));
+}
+
+/// Extensions can be made specific.
+extension IntSet on Set<int> {
+ int sum() => this.fold(0, (prev, element) => prev + element);
+}
+
+// Extensions can be private.
+extension _Shhh on Object {
+ void secret() { }
+}
+
+// Or unnamed!
+extension on Object {
+ void bar() { }
+}
+
+extension Bar on Object {
+ void bar() { }
+}
+
+
diff --git a/testing/test_package_extensions/pubspec.yaml b/testing/test_package_extensions/pubspec.yaml
new file mode 100644
index 0000000..03eca81
--- /dev/null
+++ b/testing/test_package_extensions/pubspec.yaml
@@ -0,0 +1,5 @@
+name: test_package_extensions
+version: 0.0.1
+description: A simple console application.
+environment:
+ sdk: <=3.0.0