diff --git a/lib/resources/styles.css b/lib/resources/styles.css index c967cc3bd0..cdb7346ce3 100644 --- a/lib/resources/styles.css +++ b/lib/resources/styles.css @@ -595,6 +595,22 @@ h1 .category { vertical-align: middle; } +/* Do not display "provided by X extension" text on extension pages. */ +.main-content.extension-page .from-extension { + display: none; +} + +sup.muted { + color: var(--main-sidebar-color); + font-size: 0.6em; +} + +.from-extension > span { + background-color: var(--alert-warning); + font-style: italic; + padding: 2px; +} + /* The badge under a declaration for things like "const", "read-only", etc. and for the badges inline like sealed or interface */ /* See https://github.com/dart-lang/dartdoc/blob/main/lib/src/model/feature.dart */ .feature { diff --git a/lib/src/element_type.dart b/lib/src/element_type.dart index 8e98ee06c5..c38fbb26a7 100644 --- a/lib/src/element_type.dart +++ b/lib/src/element_type.dart @@ -48,9 +48,13 @@ abstract class ElementType with CommentReferable, Nameable { String get linkedName; - /// Name with generics and nullability indication. + /// Name with generics and nullability indication, in HTML tags. String get nameWithGenerics; + /// Name with generics and nullability indication, in plain text, with + /// unescaped angle brackets. + String get nameWithGenericsPlain; + @override String get displayName => throw UnimplementedError(); @@ -103,11 +107,14 @@ class UndefinedElementType extends ElementType { return type.documentableElement!.name!; } + @override + String get linkedName => name; + @override String get nameWithGenerics => '$name$nullabilitySuffix'; @override - String get linkedName => name; + String get nameWithGenericsPlain => '$name$nullabilitySuffix'; @override Iterable get typeArguments => const []; @@ -241,6 +248,9 @@ class TypeParameterElementType extends DefinedElementType { @override String get nameWithGenerics => '$name$nullabilitySuffix'; + + @override + String get nameWithGenericsPlain => '$name$nullabilitySuffix'; } /// An [ElementType] associated with an [Element]. @@ -341,6 +351,10 @@ mixin Rendered implements ElementType { @override late final String nameWithGenerics = _renderer.renderNameWithGenerics(this); + @override + late final String nameWithGenericsPlain = + _renderer.renderNameWithGenerics(this, plain: true); + ElementTypeRenderer get _renderer; } diff --git a/lib/src/generator/templates.aot_renderers_for_html.dart b/lib/src/generator/templates.aot_renderers_for_html.dart index 10cb3be3d9..447b245ab2 100644 --- a/lib/src/generator/templates.aot_renderers_for_html.dart +++ b/lib/src/generator/templates.aot_renderers_for_html.dart @@ -555,7 +555,7 @@ String renderExtension(ExtensionTemplateData context0) { buffer.writeln(); buffer.write('''
( } buffer.write('\n\n '); if (context1.isInterface) { - if (context1.hasPublicInstanceFields) { + if (context1.hasAvailableInstanceFields) { buffer.writeln(); buffer.write('''
  • Properties
  • '''); - var context6 = context1.publicInstanceFieldsSorted; + var context6 = context1.availableInstanceFieldsSorted; for (var context7 in context6) { buffer.writeln(); buffer.write(''' '''); + buffer.write('''> + '''); buffer.write(context7.linkedName); - buffer.write(''''''); + buffer.write('\n '); + if (context7.isProvidedByExtension) { + var context8 = context7.enclosingExtension; + buffer.writeln(); + buffer.write(''' (ext)'''); + } + buffer.writeln(); + buffer.write(''' '''); } } buffer.writeln(); - if (context1.hasPublicInstanceMethods) { + if (context1.hasAvailableInstanceMethods) { buffer.writeln(); buffer.write('''
  • Methods
  • '''); - var context8 = context1.publicInstanceMethodsSorted; - for (var context9 in context8) { + var context9 = context1.availableInstanceMethodsSorted; + for (var context10 in context9) { buffer.writeln(); buffer.write(''' '''); - buffer.write(context9.linkedName); - buffer.write(''''''); + buffer.write('''> + '''); + buffer.write(context10.linkedName); + if (context10.isProvidedByExtension) { + var context11 = context10.enclosingExtension; + buffer.writeln(); + buffer.write(''' (ext)'''); + } + buffer.writeln(); + buffer.write(''' '''); } } buffer.writeln(); - if (context1.hasPublicInstanceOperators) { + if (context1.hasAvailableInstanceOperators) { buffer.writeln(); buffer.write('''
  • Operators
  • '''); - var context10 = context1.publicInstanceOperatorsSorted; - for (var context11 in context10) { + var context12 = context1.availableInstanceOperatorsSorted; + for (var context13 in context12) { buffer.writeln(); buffer.write(''' '''); - buffer.write(context11.linkedName); - buffer.write(''''''); + buffer.write('''> + '''); + buffer.write(context13.linkedName); + if (context13.isProvidedByExtension) { + var context14 = context13.enclosingExtension; + buffer.writeln(); + buffer.write(''' (ext)'''); + } + buffer.writeln(); + buffer.write(''' '''); } } } buffer.write('\n\n '); if (context1.isExtension) { - if (context1.hasPublicInstanceFields) { + if (context1.hasAvailableInstanceFields) { buffer.writeln(); buffer.write('''
  • Properties
  • '''); - var context12 = context1.publicInstanceFieldsSorted; - for (var context13 in context12) { + var context15 = context1.availableInstanceFieldsSorted; + for (var context16 in context15) { buffer.writeln(); buffer.write('''
  • '''); - buffer.write(context13.linkedName); + buffer.write(context16.linkedName); buffer.write('''
  • '''); } } buffer.writeln(); - if (context1.hasPublicInstanceMethods) { + if (context1.hasAvailableInstanceMethods) { buffer.writeln(); buffer.write('''
  • Methods
  • '''); - var context14 = context1.publicInstanceMethodsSorted; - for (var context15 in context14) { + var context17 = context1.availableInstanceMethodsSorted; + for (var context18 in context17) { buffer.writeln(); buffer.write('''
  • '''); - buffer.write(context15.linkedName); + buffer.write(context18.linkedName); buffer.write('''
  • '''); } } buffer.writeln(); - if (context1.hasPublicInstanceOperators) { + if (context1.hasAvailableInstanceOperators) { buffer.writeln(); buffer.write('''
  • Operators
  • '''); - var context16 = context1.publicInstanceOperatorsSorted; - for (var context17 in context16) { + var context19 = context1.availableInstanceOperatorsSorted; + for (var context20 in context19) { buffer.writeln(); buffer.write('''
  • '''); - buffer.write(context17.linkedName); + buffer.write(context20.linkedName); buffer.write('''
  • '''); } } @@ -1604,11 +1638,11 @@ String renderSidebarForContainer( buffer.write('''
  • Static properties
  • '''); - var context18 = context1.publicVariableStaticFieldsSorted; - for (var context19 in context18) { + var context21 = context1.publicVariableStaticFieldsSorted; + for (var context22 in context21) { buffer.writeln(); buffer.write('''
  • '''); - buffer.write(context19.linkedName); + buffer.write(context22.linkedName); buffer.write('''
  • '''); } } @@ -1618,11 +1652,11 @@ String renderSidebarForContainer( buffer.write('''
  • Static methods
  • '''); - var context20 = context1.publicStaticMethodsSorted; - for (var context21 in context20) { + var context23 = context1.publicStaticMethodsSorted; + for (var context24 in context23) { buffer.writeln(); buffer.write('''
  • '''); - buffer.write(context21.linkedName); + buffer.write(context24.linkedName); buffer.write('''
  • '''); } } @@ -1632,11 +1666,11 @@ String renderSidebarForContainer( buffer.write('''
  • Constants
  • '''); - var context22 = context1.publicConstantFieldsSorted; - for (var context23 in context22) { + var context25 = context1.publicConstantFieldsSorted; + for (var context26 in context25) { buffer.writeln(); buffer.write('''
  • '''); - buffer.write(context23.linkedName); + buffer.write(context26.linkedName); buffer.write('''
  • '''); } } @@ -1954,8 +1988,64 @@ String _renderCategory_partial_extension_4(Extension context2) => String _renderCategory_partial_constant_5(TopLevelVariable context2) => _deduplicated_lib_templates__constant_html(context2); -String _renderCategory_partial_property_6(TopLevelVariable context2) => - _deduplicated_lib_templates__property_html(context2); +String _renderCategory_partial_property_6(TopLevelVariable context2) { + final buffer = StringBuffer(); + buffer.write('''
    + '''); + buffer.write(context2.linkedName); + buffer.write(''' + '''); + buffer.write(context2.arrow); + buffer.write(' '); + buffer.write(context2.modelType.linkedName); + buffer.write(''' + '''); + buffer.write( + __renderCategory_partial_property_6_partial_categorization_0(context2)); + buffer.writeln(); + buffer.write('''
    +'''); + if (context2.isProvidedByExtension) { + var context3 = context2.enclosingExtension; + buffer.writeln(); + buffer.write('''

    + Available on '''); + buffer.write(context3.extendedElement.linkedName); + buffer.write(''', + provided by the '''); + buffer.write(context3.linkedName); + buffer.write(''' extension +

    '''); + } + buffer.write('\n '); + buffer.write(context2.oneLineDoc); + buffer.write('\n '); + buffer.write( + __renderCategory_partial_property_6_partial_attributes_1(context2)); + buffer.writeln(); + buffer.write(''' +'''); + + return buffer.toString(); +} + +String __renderCategory_partial_property_6_partial_categorization_0( + TopLevelVariable context2) => + _deduplicated_lib_templates__categorization_html(context2); + +String __renderCategory_partial_property_6_partial_attributes_1( + TopLevelVariable context2) => + _deduplicated_lib_templates__attributes_html(context2); String _renderCategory_partial_callable_7(ModelFunctionTyped context2) { final buffer = StringBuffer(); @@ -1990,8 +2080,20 @@ String _renderCategory_partial_callable_7(ModelFunctionTyped context2) { if (context2.isInherited) { buffer.write(''' class="inherited"'''); } - buffer.write('''> - '''); + buffer.write('''>'''); + if (context2.isProvidedByExtension) { + var context3 = context2.enclosingExtension; + buffer.writeln(); + buffer.write('''

    + Available on '''); + buffer.write(context3.extendedElement.linkedName); + buffer.write(''', + provided by the '''); + buffer.write(context3.linkedName); + buffer.write(''' extension +

    '''); + } + buffer.write('\n '); buffer.write(context2.oneLineDoc); buffer.write('\n '); buffer.write( @@ -2641,8 +2743,64 @@ String _renderLibrary_partial_extension_7(Extension context3) => String _renderLibrary_partial_constant_8(TopLevelVariable context3) => _deduplicated_lib_templates__constant_html(context3); -String _renderLibrary_partial_property_9(TopLevelVariable context3) => - _deduplicated_lib_templates__property_html(context3); +String _renderLibrary_partial_property_9(TopLevelVariable context3) { + final buffer = StringBuffer(); + buffer.write('''
    + '''); + buffer.write(context3.linkedName); + buffer.write(''' + '''); + buffer.write(context3.arrow); + buffer.write(' '); + buffer.write(context3.modelType.linkedName); + buffer.write(''' + '''); + buffer.write( + __renderLibrary_partial_property_9_partial_categorization_0(context3)); + buffer.writeln(); + buffer.write('''
    +'''); + if (context3.isProvidedByExtension) { + var context4 = context3.enclosingExtension; + buffer.writeln(); + buffer.write('''

    + Available on '''); + buffer.write(context4.extendedElement.linkedName); + buffer.write(''', + provided by the '''); + buffer.write(context4.linkedName); + buffer.write(''' extension +

    '''); + } + buffer.write('\n '); + buffer.write(context3.oneLineDoc); + buffer.write('\n '); + buffer + .write(__renderLibrary_partial_property_9_partial_attributes_1(context3)); + buffer.writeln(); + buffer.write(''' +'''); + + return buffer.toString(); +} + +String __renderLibrary_partial_property_9_partial_categorization_0( + TopLevelVariable context3) => + _deduplicated_lib_templates__categorization_html(context3); + +String __renderLibrary_partial_property_9_partial_attributes_1( + TopLevelVariable context3) => + _deduplicated_lib_templates__attributes_html(context3); String _renderLibrary_partial_callable_10(ModelFunctionTyped context3) { final buffer = StringBuffer(); @@ -2677,8 +2835,20 @@ String _renderLibrary_partial_callable_10(ModelFunctionTyped context3) { if (context3.isInherited) { buffer.write(''' class="inherited"'''); } - buffer.write('''> - '''); + buffer.write('''>'''); + if (context3.isProvidedByExtension) { + var context4 = context3.enclosingExtension; + buffer.writeln(); + buffer.write('''

    + Available on '''); + buffer.write(context4.extendedElement.linkedName); + buffer.write(''', + provided by the '''); + buffer.write(context4.linkedName); + buffer.write(''' extension +

    '''); + } + buffer.write('\n '); buffer.write(context3.oneLineDoc); buffer.write('\n '); buffer.write( @@ -3409,74 +3579,6 @@ String _deduplicated_lib_templates__attributes_html(ModelElement context0) { return buffer.toString(); } -String _deduplicated_lib_templates__property_html(GetterSetterCombo context0) { - final buffer = StringBuffer(); - buffer.write('''
    - '''); - buffer.write(context0.linkedName); - buffer.write(''' - '''); - buffer.write(context0.arrow); - buffer.write(' '); - buffer.write(context0.modelType.linkedName); - buffer.write(''' - '''); - buffer.write( - __deduplicated_lib_templates__property_html_partial_categorization_0( - context0)); - buffer.writeln(); - buffer.write('''
    - - '''); - buffer.write(context0.oneLineDoc); - buffer.write('\n '); - buffer.write(__deduplicated_lib_templates__property_html_partial_attributes_1( - context0)); - buffer.writeln(); - buffer.write(''' -'''); - - return buffer.toString(); -} - -String __deduplicated_lib_templates__property_html_partial_categorization_0( - GetterSetterCombo context0) { - final buffer = StringBuffer(); - if (context0.hasCategoryNames) { - var context1 = context0.displayedCategories; - for (var context2 in context1) { - buffer.write('\n '); - buffer.write(context2!.categoryLabel); - } - } - buffer.writeln(); - - return buffer.toString(); -} - -String __deduplicated_lib_templates__property_html_partial_attributes_1( - GetterSetterCombo context0) { - final buffer = StringBuffer(); - if (context0.hasAttributes) { - buffer.write('''
    '''); - buffer.write(context0.attributesAsString); - buffer.write('''
    '''); - } - buffer.writeln(); - - return buffer.toString(); -} - String _deduplicated_lib_templates__typedef_html(Typedef context0) { final buffer = StringBuffer(); if (context0.isCallable) { @@ -4053,7 +4155,7 @@ String _deduplicated_lib_templates__constructors_html(Constructable context0) { String _deduplicated_lib_templates__instance_fields_html(Container context0) { final buffer = StringBuffer(); - if (context0.hasPublicInstanceFields) { + if (context0.hasAvailableInstanceFields) { buffer.writeln(); buffer.write('''

    Properties

    '''); - var context1 = context0.publicInstanceFieldsSorted; + var context1 = context0.availableInstanceFieldsSorted; for (var context2 in context1) { buffer.write('\n '); buffer.write( @@ -4075,6 +4177,7 @@ String _deduplicated_lib_templates__instance_fields_html(Container context0) { buffer.write('''
    '''); } + buffer.writeln(); return buffer.toString(); } @@ -4107,8 +4210,20 @@ String __deduplicated_lib_templates__instance_fields_html_partial_property_0( if (context1.isInherited) { buffer.write(''' class="inherited"'''); } - buffer.write('''> - '''); + buffer.write('''>'''); + if (context1.isProvidedByExtension) { + var context2 = context1.enclosingExtension; + buffer.writeln(); + buffer.write('''

    + Available on '''); + buffer.write(context2.extendedElement.linkedName); + buffer.write(''', + provided by the '''); + buffer.write(context2.linkedName); + buffer.write(''' extension +

    '''); + } + buffer.write('\n '); buffer.write(context1.oneLineDoc); buffer.write('\n '); buffer.write( @@ -4153,7 +4268,7 @@ String String _deduplicated_lib_templates__instance_methods_html(Container context0) { final buffer = StringBuffer(); - if (context0.hasPublicInstanceMethods) { + if (context0.hasAvailableInstanceMethods) { buffer.writeln(); buffer.write('''

    Methods

    '''); - var context1 = context0.publicInstanceMethodsSorted; + var context1 = context0.availableInstanceMethodsSorted; for (var context2 in context1) { buffer.write('\n '); buffer.write( @@ -4214,8 +4329,20 @@ String __deduplicated_lib_templates__instance_methods_html_partial_callable_0( if (context1.isInherited) { buffer.write(''' class="inherited"'''); } - buffer.write('''> - '''); + buffer.write('''>'''); + if (context1.isProvidedByExtension) { + var context2 = context1.enclosingExtension; + buffer.writeln(); + buffer.write('''

    + Available on '''); + buffer.write(context2.extendedElement.linkedName); + buffer.write(''', + provided by the '''); + buffer.write(context2.linkedName); + buffer.write(''' extension +

    '''); + } + buffer.write('\n '); buffer.write(context1.oneLineDoc); buffer.write('\n '); buffer.write( @@ -4261,7 +4388,7 @@ String String _deduplicated_lib_templates__instance_operators_html( Container context0) { final buffer = StringBuffer(); - if (context0.hasPublicInstanceOperators) { + if (context0.hasAvailableInstanceOperators) { buffer.writeln(); buffer.write('''

    Operators

    '''); - var context1 = context0.publicInstanceOperatorsSorted; + var context1 = context0.availableInstanceOperatorsSorted; for (var context2 in context1) { buffer.write('\n '); buffer.write( @@ -4322,8 +4449,20 @@ String __deduplicated_lib_templates__instance_operators_html_partial_callable_0( if (context1.isInherited) { buffer.write(''' class="inherited"'''); } - buffer.write('''> - '''); + buffer.write('''>'''); + if (context1.isProvidedByExtension) { + var context2 = context1.enclosingExtension; + buffer.writeln(); + buffer.write('''

    + Available on '''); + buffer.write(context2.extendedElement.linkedName); + buffer.write(''', + provided by the '''); + buffer.write(context2.linkedName); + buffer.write(''' extension +

    '''); + } + buffer.write('\n '); buffer.write(context1.oneLineDoc); buffer.write('\n '); buffer.write( @@ -4417,8 +4556,20 @@ String __deduplicated_lib_templates__static_properties_html_partial_property_0( if (context1.isInherited) { buffer.write(''' class="inherited"'''); } - buffer.write('''> - '''); + buffer.write('''>'''); + if (context1.isProvidedByExtension) { + var context2 = context1.enclosingExtension; + buffer.writeln(); + buffer.write('''

    + Available on '''); + buffer.write(context2.extendedElement.linkedName); + buffer.write(''', + provided by the '''); + buffer.write(context2.linkedName); + buffer.write(''' extension +

    '''); + } + buffer.write('\n '); buffer.write(context1.oneLineDoc); buffer.write('\n '); buffer.write( @@ -4519,8 +4670,20 @@ String __deduplicated_lib_templates__static_methods_html_partial_callable_0( if (context1.isInherited) { buffer.write(''' class="inherited"'''); } - buffer.write('''> - '''); + buffer.write('''>'''); + if (context1.isProvidedByExtension) { + var context2 = context1.enclosingExtension; + buffer.writeln(); + buffer.write('''

    + Available on '''); + buffer.write(context2.extendedElement.linkedName); + buffer.write(''', + provided by the '''); + buffer.write(context2.linkedName); + buffer.write(''' extension +

    '''); + } + buffer.write('\n '); buffer.write(context1.oneLineDoc); buffer.write('\n '); buffer.write( diff --git a/lib/src/generator/templates.runtime_renderers.dart b/lib/src/generator/templates.runtime_renderers.dart index 04f420abe2..effe17e03a 100644 --- a/lib/src/generator/templates.runtime_renderers.dart +++ b/lib/src/generator/templates.runtime_renderers.dart @@ -2619,30 +2619,53 @@ class _Renderer_Container extends RendererBase { parent: r)); }, ), - 'allElements': Property( - getValue: (CT_ c) => c.allElements, + 'allModelElements': Property( + getValue: (CT_ c) => c.allModelElements, renderVariable: (CT_ c, Property self, List remainingNames) => self.renderSimpleVariable( - c, remainingNames, 'Set'), + c, remainingNames, 'Iterable'), renderIterable: (CT_ c, RendererBase r, List ast, StringSink sink) { - return c.allElements.map((e) => renderSimple( + return c.allModelElements.map((e) => _render_ModelElement( e, ast, r.template, sink, - parent: r, getters: _invisibleGetters['Element']!)); + parent: r)); }, ), - 'allModelElements': Property( - getValue: (CT_ c) => c.allModelElements, + 'availableInstanceFieldsSorted': Property( + getValue: (CT_ c) => c.availableInstanceFieldsSorted, renderVariable: (CT_ c, Property self, List remainingNames) => self.renderSimpleVariable( - c, remainingNames, 'Iterable'), + c, remainingNames, 'List'), renderIterable: (CT_ c, RendererBase r, List ast, StringSink sink) { - return c.allModelElements.map((e) => _render_ModelElement( - e, ast, r.template, sink, - parent: r)); + return c.availableInstanceFieldsSorted.map((e) => + _render_Field(e, ast, r.template, sink, parent: r)); + }, + ), + 'availableInstanceMethodsSorted': Property( + getValue: (CT_ c) => c.availableInstanceMethodsSorted, + renderVariable: (CT_ c, Property self, + List remainingNames) => + self.renderSimpleVariable( + c, remainingNames, 'List'), + renderIterable: (CT_ c, RendererBase r, + List ast, StringSink sink) { + return c.availableInstanceMethodsSorted.map((e) => + _render_Method(e, ast, r.template, sink, parent: r)); + }, + ), + 'availableInstanceOperatorsSorted': Property( + getValue: (CT_ c) => c.availableInstanceOperatorsSorted, + renderVariable: (CT_ c, Property self, + List remainingNames) => + self.renderSimpleVariable( + c, remainingNames, 'List'), + renderIterable: (CT_ c, RendererBase r, + List ast, StringSink sink) { + return c.availableInstanceOperatorsSorted.map((e) => + _render_Operator(e, ast, r.template, sink, parent: r)); }, ), 'belowSidebarPath': Property( @@ -2760,6 +2783,27 @@ class _Renderer_Container extends RendererBase { parent: r); }, ), + 'hasAvailableInstanceFields': Property( + getValue: (CT_ c) => c.hasAvailableInstanceFields, + renderVariable: (CT_ c, Property self, + List remainingNames) => + self.renderSimpleVariable(c, remainingNames, 'bool'), + getBool: (CT_ c) => c.hasAvailableInstanceFields, + ), + 'hasAvailableInstanceMethods': Property( + getValue: (CT_ c) => c.hasAvailableInstanceMethods, + renderVariable: (CT_ c, Property self, + List remainingNames) => + self.renderSimpleVariable(c, remainingNames, 'bool'), + getBool: (CT_ c) => c.hasAvailableInstanceMethods, + ), + 'hasAvailableInstanceOperators': Property( + getValue: (CT_ c) => c.hasAvailableInstanceOperators, + renderVariable: (CT_ c, Property self, + List remainingNames) => + self.renderSimpleVariable(c, remainingNames, 'bool'), + getBool: (CT_ c) => c.hasAvailableInstanceOperators, + ), 'hasInstanceFields': Property( getValue: (CT_ c) => c.hasInstanceFields, renderVariable: (CT_ c, Property self, @@ -2795,27 +2839,6 @@ class _Renderer_Container extends RendererBase { self.renderSimpleVariable(c, remainingNames, 'bool'), getBool: (CT_ c) => c.hasPublicEnumValues, ), - 'hasPublicInstanceFields': Property( - getValue: (CT_ c) => c.hasPublicInstanceFields, - renderVariable: (CT_ c, Property self, - List remainingNames) => - self.renderSimpleVariable(c, remainingNames, 'bool'), - getBool: (CT_ c) => c.hasPublicInstanceFields, - ), - 'hasPublicInstanceMethods': Property( - getValue: (CT_ c) => c.hasPublicInstanceMethods, - renderVariable: (CT_ c, Property self, - List remainingNames) => - self.renderSimpleVariable(c, remainingNames, 'bool'), - getBool: (CT_ c) => c.hasPublicInstanceMethods, - ), - 'hasPublicInstanceOperators': Property( - getValue: (CT_ c) => c.hasPublicInstanceOperators, - renderVariable: (CT_ c, Property self, - List remainingNames) => - self.renderSimpleVariable(c, remainingNames, 'bool'), - getBool: (CT_ c) => c.hasPublicInstanceOperators, - ), 'hasPublicStaticFields': Property( getValue: (CT_ c) => c.hasPublicStaticFields, renderVariable: (CT_ c, Property self, @@ -2971,42 +2994,6 @@ class _Renderer_Container extends RendererBase { self.renderSimpleVariable(c, remainingNames, 'bool'), getBool: (CT_ c) => c.publicInheritedInstanceOperators, ), - 'publicInstanceFieldsSorted': Property( - getValue: (CT_ c) => c.publicInstanceFieldsSorted, - renderVariable: (CT_ c, Property self, - List remainingNames) => - self.renderSimpleVariable( - c, remainingNames, 'List'), - renderIterable: (CT_ c, RendererBase r, - List ast, StringSink sink) { - return c.publicInstanceFieldsSorted.map((e) => - _render_Field(e, ast, r.template, sink, parent: r)); - }, - ), - 'publicInstanceMethodsSorted': Property( - getValue: (CT_ c) => c.publicInstanceMethodsSorted, - renderVariable: (CT_ c, Property self, - List remainingNames) => - self.renderSimpleVariable( - c, remainingNames, 'List'), - renderIterable: (CT_ c, RendererBase r, - List ast, StringSink sink) { - return c.publicInstanceMethodsSorted.map((e) => - _render_Method(e, ast, r.template, sink, parent: r)); - }, - ), - 'publicInstanceOperatorsSorted': Property( - getValue: (CT_ c) => c.publicInstanceOperatorsSorted, - renderVariable: (CT_ c, Property self, - List remainingNames) => - self.renderSimpleVariable( - c, remainingNames, 'List'), - renderIterable: (CT_ c, RendererBase r, - List ast, StringSink sink) { - return c.publicInstanceOperatorsSorted.map((e) => - _render_Operator(e, ast, r.template, sink, parent: r)); - }, - ), 'publicStaticFieldsSorted': Property( getValue: (CT_ c) => c.publicStaticFieldsSorted, renderVariable: (CT_ c, Property self, @@ -4144,6 +4131,29 @@ class _Renderer_ElementType extends RendererBase { parent: r); }, ), + 'nameWithGenericsPlain': Property( + getValue: (CT_ c) => c.nameWithGenericsPlain, + renderVariable: + (CT_ c, Property self, List remainingNames) { + if (remainingNames.isEmpty) { + return self.getValue(c).toString(); + } + var name = remainingNames.first; + var nextProperty = + _Renderer_String.propertyMap().getValue(name); + return nextProperty.renderVariable( + self.getValue(c) as String, + nextProperty, + [...remainingNames.skip(1)]); + }, + isNullValue: (CT_ c) => false, + renderValue: (CT_ c, RendererBase r, + List ast, StringSink sink) { + _render_String( + c.nameWithGenericsPlain, ast, r.template, sink, + parent: r); + }, + ), 'nullabilitySuffix': Property( getValue: (CT_ c) => c.nullabilitySuffix, renderVariable: @@ -4527,6 +4537,42 @@ class _Renderer_Extension extends RendererBase { self.renderSimpleVariable(c, remainingNames, 'bool'), getBool: (CT_ c) => c.alwaysApplies, ), + 'availableInstanceFieldsSorted': Property( + getValue: (CT_ c) => c.availableInstanceFieldsSorted, + renderVariable: (CT_ c, Property self, + List remainingNames) => + self.renderSimpleVariable( + c, remainingNames, 'List'), + renderIterable: (CT_ c, RendererBase r, + List ast, StringSink sink) { + return c.availableInstanceFieldsSorted.map((e) => + _render_Field(e, ast, r.template, sink, parent: r)); + }, + ), + 'availableInstanceMethodsSorted': Property( + getValue: (CT_ c) => c.availableInstanceMethodsSorted, + renderVariable: (CT_ c, Property self, + List remainingNames) => + self.renderSimpleVariable( + c, remainingNames, 'List'), + renderIterable: (CT_ c, RendererBase r, + List ast, StringSink sink) { + return c.availableInstanceMethodsSorted.map((e) => + _render_Method(e, ast, r.template, sink, parent: r)); + }, + ), + 'availableInstanceOperatorsSorted': Property( + getValue: (CT_ c) => c.availableInstanceOperatorsSorted, + renderVariable: (CT_ c, Property self, + List remainingNames) => + self.renderSimpleVariable( + c, remainingNames, 'List'), + renderIterable: (CT_ c, RendererBase r, + List ast, StringSink sink) { + return c.availableInstanceOperatorsSorted.map((e) => + _render_Operator(e, ast, r.template, sink, parent: r)); + }, + ), 'declaredFields': Property( getValue: (CT_ c) => c.declaredFields, renderVariable: (CT_ c, Property self, @@ -4610,6 +4656,18 @@ class _Renderer_Extension extends RendererBase { parent: r); }, ), + 'instanceMethods': Property( + getValue: (CT_ c) => c.instanceMethods, + renderVariable: (CT_ c, Property self, + List remainingNames) => + self.renderSimpleVariable( + c, remainingNames, 'Iterable'), + renderIterable: (CT_ c, RendererBase r, + List ast, StringSink sink) { + return c.instanceMethods.map((e) => + _render_Method(e, ast, r.template, sink, parent: r)); + }, + ), 'kind': Property( getValue: (CT_ c) => c.kind, renderVariable: (CT_ c, Property self, @@ -5704,6 +5762,29 @@ class _Renderer_Field extends RendererBase { parent: r); }, ), + 'enclosingExtension': Property( + getValue: (CT_ c) => c.enclosingExtension, + renderVariable: + (CT_ c, Property self, List remainingNames) { + if (remainingNames.isEmpty) { + return self.getValue(c).toString(); + } + var name = remainingNames.first; + var nextProperty = + _Renderer_Extension.propertyMap().getValue(name); + return nextProperty.renderVariable( + self.getValue(c) as Extension, + nextProperty, + [...remainingNames.skip(1)]); + }, + isNullValue: (CT_ c) => false, + renderValue: (CT_ c, RendererBase r, + List ast, StringSink sink) { + _render_Extension( + c.enclosingExtension, ast, r.template, sink, + parent: r); + }, + ), 'fileName': Property( getValue: (CT_ c) => c.fileName, renderVariable: @@ -5826,6 +5907,13 @@ class _Renderer_Field extends RendererBase { self.renderSimpleVariable(c, remainingNames, 'bool'), getBool: (CT_ c) => c.isLate, ), + 'isProvidedByExtension': Property( + getValue: (CT_ c) => c.isProvidedByExtension, + renderVariable: (CT_ c, Property self, + List remainingNames) => + self.renderSimpleVariable(c, remainingNames, 'bool'), + getBool: (CT_ c) => c.isProvidedByExtension, + ), 'isStatic': Property( getValue: (CT_ c) => c.isStatic, renderVariable: (CT_ c, Property self, @@ -7055,6 +7143,42 @@ class _Renderer_InheritingContainer extends RendererBase { parent: r)); }, ), + 'availableInstanceFieldsSorted': Property( + getValue: (CT_ c) => c.availableInstanceFieldsSorted, + renderVariable: (CT_ c, Property self, + List remainingNames) => + self.renderSimpleVariable( + c, remainingNames, 'List'), + renderIterable: (CT_ c, RendererBase r, + List ast, StringSink sink) { + return c.availableInstanceFieldsSorted.map((e) => + _render_Field(e, ast, r.template, sink, parent: r)); + }, + ), + 'availableInstanceMethodsSorted': Property( + getValue: (CT_ c) => c.availableInstanceMethodsSorted, + renderVariable: (CT_ c, Property self, + List remainingNames) => + self.renderSimpleVariable( + c, remainingNames, 'List'), + renderIterable: (CT_ c, RendererBase r, + List ast, StringSink sink) { + return c.availableInstanceMethodsSorted.map((e) => + _render_Method(e, ast, r.template, sink, parent: r)); + }, + ), + 'availableInstanceOperatorsSorted': Property( + getValue: (CT_ c) => c.availableInstanceOperatorsSorted, + renderVariable: (CT_ c, Property self, + List remainingNames) => + self.renderSimpleVariable( + c, remainingNames, 'List'), + renderIterable: (CT_ c, RendererBase r, + List ast, StringSink sink) { + return c.availableInstanceOperatorsSorted.map((e) => + _render_Operator(e, ast, r.template, sink, parent: r)); + }, + ), 'constantFields': Property( getValue: (CT_ c) => c.constantFields, renderVariable: (CT_ c, Property self, @@ -9142,6 +9266,29 @@ class _Renderer_Method extends RendererBase { parent: r); }, ), + 'enclosingExtension': Property( + getValue: (CT_ c) => c.enclosingExtension, + renderVariable: + (CT_ c, Property self, List remainingNames) { + if (remainingNames.isEmpty) { + return self.getValue(c).toString(); + } + var name = remainingNames.first; + var nextProperty = + _Renderer_Extension.propertyMap().getValue(name); + return nextProperty.renderVariable( + self.getValue(c) as Extension, + nextProperty, + [...remainingNames.skip(1)]); + }, + isNullValue: (CT_ c) => false, + renderValue: (CT_ c, RendererBase r, + List ast, StringSink sink) { + _render_Extension( + c.enclosingExtension, ast, r.template, sink, + parent: r); + }, + ), 'fullkind': Property( getValue: (CT_ c) => c.fullkind, renderVariable: @@ -9206,6 +9353,13 @@ class _Renderer_Method extends RendererBase { self.renderSimpleVariable(c, remainingNames, 'bool'), getBool: (CT_ c) => c.isOperator, ), + 'isProvidedByExtension': Property( + getValue: (CT_ c) => c.isProvidedByExtension, + renderVariable: (CT_ c, Property self, + List remainingNames) => + self.renderSimpleVariable(c, remainingNames, 'bool'), + getBool: (CT_ c) => c.isProvidedByExtension, + ), 'isStatic': Property( getValue: (CT_ c) => c.isStatic, renderVariable: (CT_ c, Property self, @@ -10942,6 +11096,29 @@ class _Renderer_ModelFunctionTyped extends RendererBase { parent: r); }, ), + 'enclosingExtension': Property( + getValue: (CT_ c) => c.enclosingExtension, + renderVariable: + (CT_ c, Property self, List remainingNames) { + if (remainingNames.isEmpty) { + return self.getValue(c).toString(); + } + var name = remainingNames.first; + var nextProperty = + _Renderer_Extension.propertyMap().getValue(name); + return nextProperty.renderVariable( + self.getValue(c) as Extension, + nextProperty, + [...remainingNames.skip(1)]); + }, + isNullValue: (CT_ c) => false, + renderValue: (CT_ c, RendererBase r, + List ast, StringSink sink) { + _render_Extension( + c.enclosingExtension, ast, r.template, sink, + parent: r); + }, + ), 'filePath': Property( getValue: (CT_ c) => c.filePath, renderVariable: @@ -10992,6 +11169,13 @@ class _Renderer_ModelFunctionTyped extends RendererBase { self.renderSimpleVariable(c, remainingNames, 'bool'), getBool: (CT_ c) => c.isInherited, ), + 'isProvidedByExtension': Property( + getValue: (CT_ c) => c.isProvidedByExtension, + renderVariable: (CT_ c, Property self, + List remainingNames) => + self.renderSimpleVariable(c, remainingNames, 'bool'), + getBool: (CT_ c) => c.isProvidedByExtension, + ), 'kind': Property( getValue: (CT_ c) => c.kind, renderVariable: (CT_ c, Property self, @@ -12129,7 +12313,7 @@ class _Renderer_Package extends RendererBase { } } -String renderSearchPage(PackageTemplateData context, Template template) { +String renderIndex(PackageTemplateData context, Template template) { var buffer = StringBuffer(); _render_PackageTemplateData(context, template.ast, template, buffer); return buffer.toString(); @@ -12367,13 +12551,13 @@ class _Renderer_PackageTemplateData extends RendererBase { } } -String renderIndex(PackageTemplateData context, Template template) { +String renderError(PackageTemplateData context, Template template) { var buffer = StringBuffer(); _render_PackageTemplateData(context, template.ast, template, buffer); return buffer.toString(); } -String renderError(PackageTemplateData context, Template template) { +String renderSearchPage(PackageTemplateData context, Template template) { var buffer = StringBuffer(); _render_PackageTemplateData(context, template.ast, template, buffer); return buffer.toString(); @@ -12967,6 +13151,29 @@ class _Renderer_Rendered extends RendererBase { parent: r); }, ), + 'nameWithGenericsPlain': Property( + getValue: (CT_ c) => c.nameWithGenericsPlain, + renderVariable: + (CT_ c, Property self, List remainingNames) { + if (remainingNames.isEmpty) { + return self.getValue(c).toString(); + } + var name = remainingNames.first; + var nextProperty = + _Renderer_String.propertyMap().getValue(name); + return nextProperty.renderVariable( + self.getValue(c) as String, + nextProperty, + [...remainingNames.skip(1)]); + }, + isNullValue: (CT_ c) => false, + renderValue: (CT_ c, RendererBase r, + List ast, StringSink sink) { + _render_String( + c.nameWithGenericsPlain, ast, r.template, sink, + parent: r); + }, + ), }) as Map>; _Renderer_Rendered(Rendered context, RendererBase? parent, @@ -14505,6 +14712,29 @@ class _Renderer_TopLevelVariable extends RendererBase { parent: r); }, ), + 'enclosingExtension': Property( + getValue: (CT_ c) => c.enclosingExtension, + renderVariable: + (CT_ c, Property self, List remainingNames) { + if (remainingNames.isEmpty) { + return self.getValue(c).toString(); + } + var name = remainingNames.first; + var nextProperty = + _Renderer_Extension.propertyMap().getValue(name); + return nextProperty.renderVariable( + self.getValue(c) as Extension, + nextProperty, + [...remainingNames.skip(1)]); + }, + isNullValue: (CT_ c) => false, + renderValue: (CT_ c, RendererBase r, + List ast, StringSink sink) { + _render_Extension( + c.enclosingExtension, ast, r.template, sink, + parent: r); + }, + ), 'filePath': Property( getValue: (CT_ c) => c.filePath, renderVariable: @@ -14598,6 +14828,13 @@ class _Renderer_TopLevelVariable extends RendererBase { self.renderSimpleVariable(c, remainingNames, 'bool'), getBool: (CT_ c) => c.isLate, ), + 'isProvidedByExtension': Property( + getValue: (CT_ c) => c.isProvidedByExtension, + renderVariable: (CT_ c, Property self, + List remainingNames) => + self.renderSimpleVariable(c, remainingNames, 'bool'), + getBool: (CT_ c) => c.isProvidedByExtension, + ), 'kind': Property( getValue: (CT_ c) => c.kind, renderVariable: (CT_ c, Property self, diff --git a/lib/src/model/accessor.dart b/lib/src/model/accessor.dart index de43e1c4ff..c2e1b9267c 100644 --- a/lib/src/model/accessor.dart +++ b/lib/src/model/accessor.dart @@ -120,7 +120,7 @@ class Accessor extends ModelElement { return getModelForElement(element.enclosingElement.enclosingElement!); } - return packageGraph.getModelFor(element.enclosingElement, library); + return getModelFor(element.enclosingElement, library); } @override @@ -145,9 +145,7 @@ class Accessor extends ModelElement { bool get isCanonical => enclosingCombo.isCanonical; @override - String? get href { - return enclosingCombo.href; - } + String? get href => enclosingCombo.href; bool get isGetter => element.isGetter; @@ -171,6 +169,12 @@ class Accessor extends ModelElement { /// A getter or setter that is a member of a [Container]. class ContainerAccessor extends Accessor with ContainerMember, Inheritable { + ContainerAccessor(super.element, super.library, super.packageGraph, + [Container? enclosingElement]) + : isInherited = false { + _enclosingElement = enclosingElement ?? super.enclosingElement as Container; + } + /// The index and values fields are never declared, and must be special cased. bool get _isEnumSynthetic => enclosingCombo is EnumField && (name == 'index' || name == 'values'); @@ -192,11 +196,6 @@ class ContainerAccessor extends Accessor with ContainerMember, Inheritable { @override bool get isCovariant => isSetter && parameters.first.isCovariant; - ContainerAccessor(super.element, super.library, super.packageGraph) - : isInherited = false { - _enclosingElement = super.enclosingElement as Container; - } - ContainerAccessor.inherited( super.element, super.library, super.packageGraph, this._enclosingElement, {super.originalMember}) diff --git a/lib/src/model/container.dart b/lib/src/model/container.dart index de5076d723..881df0c938 100644 --- a/lib/src/model/container.dart +++ b/lib/src/model/container.dart @@ -49,6 +49,8 @@ abstract class Container extends ModelElement /// Whether this is a mixin. bool get isMixin => element is MixinElement; + /// The model elements of all of the members of this container, including + /// declared and inherited ones. Iterable get allModelElements => [ ...instanceMethods, ...instanceFields, @@ -62,16 +64,15 @@ abstract class Container extends ModelElement late final List allCanonicalModelElements = allModelElements.where((e) => e.isCanonical).toList(growable: false); - /// All methods, including operators and statics, declared as part of this - /// [Container]. + /// All methods, including operators and statics, declared on [element]. /// /// [declaredMethods] must be the union of [instanceMethods], /// [staticMethods], and [instanceOperators]. Iterable get declaredMethods; - Iterable get instanceMethods => declaredMethods - .where((m) => !m.isStatic && !m.isOperator) - .toList(growable: false); + /// The instance methods available on [element], including declared and + /// inherited ones. + Iterable get instanceMethods; /// Whether all instance fields are inherited. bool get publicInheritedInstanceFields => false; @@ -87,11 +88,10 @@ abstract class Container extends ModelElement List get publicConstructorsSorted => const []; - @nonVirtual - bool get hasPublicInstanceMethods => instanceMethods.any((e) => e.isPublic); + bool get hasAvailableInstanceMethods => + availableInstanceMethodsSorted.isNotEmpty; - List get publicInstanceMethodsSorted => - instanceMethods.wherePublic.toList(growable: false)..sort(); + List get availableInstanceMethodsSorted; @nonVirtual late final List declaredOperators = @@ -100,14 +100,13 @@ abstract class Container extends ModelElement @override ModelElement get enclosingElement; + /// The instance operators declared on [element]. Iterable get instanceOperators => declaredOperators; - @nonVirtual - bool get hasPublicInstanceOperators => - instanceOperators.any((e) => e.isPublic); + bool get hasAvailableInstanceOperators => + availableInstanceOperatorsSorted.isNotEmpty; - List get publicInstanceOperatorsSorted => - instanceOperators.wherePublic.toList(growable: false)..sort(); + List get availableInstanceOperatorsSorted; /// Fields fully declared in this [Container]. Iterable get declaredFields; @@ -119,10 +118,9 @@ abstract class Container extends ModelElement bool get hasInstanceFields => instanceFields.isNotEmpty; @nonVirtual - bool get hasPublicInstanceFields => instanceFields.any((e) => e.isPublic); + bool get hasAvailableInstanceFields => instanceFields.any((e) => e.isPublic); - List get publicInstanceFieldsSorted => - instanceFields.wherePublic.toList(growable: false)..sort(byName); + List get availableInstanceFieldsSorted; Iterable get constantFields => declaredFields.where((f) => f.isConst); @@ -143,17 +141,19 @@ abstract class Container extends ModelElement /// `ContainerSidebar` Mustache template needs to refer to this field. bool get hasPublicEnumValues => publicEnumValues.isNotEmpty; + /// The instance accessors declared on [element]. Iterable get instanceAccessors => instanceFields.expand((f) => f.allAccessors); + /// The static accessors declared on [element]. Iterable get staticAccessors => staticFields.expand((f) => f.allAccessors); /// This container might be canonical for elements it does not contain. /// See [Inheritable.canonicalEnclosingContainer]. - bool containsElement(Element? element) => allElements.contains(element); + bool containsElement(Element? element) => _allElements.contains(element); - late final Set allElements = + late final Set _allElements = allModelElements.map((e) => e.element).toSet(); late final Map> _membersByName = () { @@ -199,6 +199,7 @@ abstract class Container extends ModelElement List get publicVariableStaticFieldsSorted => variableStaticFields.wherePublic.toList(growable: false)..sort(); + /// The static methods declared on [element]. Iterable get staticMethods => declaredMethods.where((m) => m.isStatic); diff --git a/lib/src/model/extension.dart b/lib/src/model/extension.dart index bb6fef4207..9b10f5579c 100644 --- a/lib/src/model/extension.dart +++ b/lib/src/model/extension.dart @@ -8,6 +8,7 @@ import 'package:analyzer/dart/element/type.dart'; import 'package:dartdoc/src/element_type.dart'; import 'package:dartdoc/src/model/comment_referable.dart'; import 'package:dartdoc/src/model/model.dart'; +import 'package:dartdoc/src/model_utils.dart'; import 'package:meta/meta.dart'; /// Static extension on a given type, containing methods (including getters, @@ -68,11 +69,29 @@ class Extension extends Container { @override Kind get kind => Kind.extension; + @override + List get availableInstanceFieldsSorted => + instanceFields.wherePublic.toList(growable: false)..sort(byName); + @override late final List declaredMethods = element.methods - .map((e) => getModelFor(e, library) as Method) + .map((e) => getModelFor(e, library, enclosingContainer: this) as Method) .toList(growable: false); + @override + Iterable get instanceMethods => + declaredMethods.where((m) => !m.isStatic && !m.isOperator); + + @override + late final List availableInstanceMethodsSorted = [ + ...instanceMethods.wherePublic, + ]..sort(); + + @override + late final List availableInstanceOperatorsSorted = [ + ...instanceOperators.wherePublic, + ]..sort(); + @override String get name => element.name == null ? '' : super.name; @@ -81,11 +100,13 @@ class Extension extends Container { ContainerAccessor? getter, setter; final fieldGetter = field.getter; if (fieldGetter != null) { - getter = ContainerAccessor(fieldGetter, library, packageGraph); + getter = ModelElement.for_(fieldGetter, library, packageGraph, + enclosingContainer: this) as ContainerAccessor; } final fieldSetter = field.setter; if (fieldSetter != null) { - setter = ContainerAccessor(fieldSetter, library, packageGraph); + setter = ModelElement.for_(fieldSetter, library, packageGraph, + enclosingContainer: this) as ContainerAccessor; } return getModelForPropertyInducingElement(field, library, getter: getter, setter: setter, enclosingContainer: this) as Field; diff --git a/lib/src/model/extension_type.dart b/lib/src/model/extension_type.dart index 3a50e9ee13..5616fa2a81 100644 --- a/lib/src/model/extension_type.dart +++ b/lib/src/model/extension_type.dart @@ -43,11 +43,11 @@ class ExtensionType extends InheritingContainer with Constructable { ContainerAccessor? getter, setter; final fieldGetter = field.getter; if (fieldGetter != null) { - getter = ContainerAccessor(fieldGetter, library, packageGraph); + getter = ContainerAccessor(fieldGetter, library, packageGraph, this); } final fieldSetter = field.setter; if (fieldSetter != null) { - setter = ContainerAccessor(fieldSetter, library, packageGraph); + setter = ContainerAccessor(fieldSetter, library, packageGraph, this); } return getModelForPropertyInducingElement(field, library, getter: getter, setter: setter) as Field; diff --git a/lib/src/model/field.dart b/lib/src/model/field.dart index afe87bda33..5872adccb6 100644 --- a/lib/src/model/field.dart +++ b/lib/src/model/field.dart @@ -40,6 +40,19 @@ class Field extends ModelElement setter?.enclosingCombo = this; } + Field.providedByExtension( + this.element, + this.enclosingElement, + super.library, + super.packageGraph, + this.getter, + this.setter, + ) : isInherited = false, + assert(getter != null || setter != null) { + getter?.enclosingCombo = this; + setter?.enclosingCombo = this; + } + Field.inherited( this.element, this.enclosingElement, @@ -112,6 +125,12 @@ class Field extends ModelElement String get fullkind => element.isAbstract ? 'abstract $kind' : kind.toString(); + bool get isProvidedByExtension => + element.enclosingElement is ExtensionElement; + + /// The [enclosingElement], which is expected to be an [Extension]. + Extension get enclosingExtension => enclosingElement as Extension; + @override Set get attributes { var allAttributes = {...super.attributes, ...comboAttributes}; diff --git a/lib/src/model/inheriting_container.dart b/lib/src/model/inheriting_container.dart index 4fc64ce8b5..6705cd5d26 100644 --- a/lib/src/model/inheriting_container.dart +++ b/lib/src/model/inheriting_container.dart @@ -333,13 +333,59 @@ abstract class InheritingContainer extends Container { Iterable get instanceFields => allFields.where((f) => !f.isStatic); @override - Iterable get instanceMethods => - [...super.instanceMethods, ...inheritedMethods]; + late final List availableInstanceFieldsSorted = [ + ...instanceFields.wherePublic, + ..._extensionInstanceFields.wherePublic, + ]..sort(); + + List get _extensionInstanceFields => [ + for (var extension in potentiallyApplicableExtensionsSorted) + for (var field in extension.instanceFields) + getModelForPropertyInducingElement( + field.element, + library, + enclosingContainer: extension, + getter: field.getter, + setter: field.setter, + ) as Field, + ]; + + @override + Iterable get instanceMethods => [ + ...declaredMethods.where((m) => !m.isStatic && !m.isOperator), + ...inheritedMethods, + ]; + + @override + late final List availableInstanceMethodsSorted = [ + ...instanceMethods.wherePublic, + ..._extensionInstanceMethods.wherePublic, + ]..sort(); + + List get _extensionInstanceMethods => [ + for (var extension in potentiallyApplicableExtensionsSorted) + for (var method in extension.instanceMethods) + getModelFor(method.element, library, enclosingContainer: extension) + as Method, + ]; @override Iterable get instanceOperators => [...super.instanceOperators, ...inheritedOperators]; + @override + late final List availableInstanceOperatorsSorted = [ + ...instanceOperators.wherePublic, + ..._extensionInstanceOperators.wherePublic, + ]..sort(); + + List get _extensionInstanceOperators => [ + for (var extension in potentiallyApplicableExtensionsSorted) + for (var operator in extension.instanceOperators) + getModelFor(operator.element, library, + enclosingContainer: extension) as Operator, + ]; + bool get isAbstract; bool get isBase; diff --git a/lib/src/model/method.dart b/lib/src/model/method.dart index 9f6e4012e3..a77d8b3757 100644 --- a/lib/src/model/method.dart +++ b/lib/src/model/method.dart @@ -35,6 +35,16 @@ class Method extends ModelElement _calcTypeParameters(); } + Method.providedByExtension( + this.element, + this._enclosingContainer, + super.library, + super.packageGraph, { + ExecutableMember? super.originalMember, + }) : _isInherited = false { + _calcTypeParameters(); + } + void _calcTypeParameters() { typeParameters = element.typeParameters.map((f) { return getModelFor(f, library) as TypeParameter; @@ -83,6 +93,12 @@ class Method extends ModelElement bool get isOperator => false; + bool get isProvidedByExtension => + element.enclosingElement is ExtensionElement; + + /// The [enclosingElement], which is expected to be an [Extension]. + Extension get enclosingExtension => enclosingElement as Extension; + @override Set get attributes => { ...super.attributes, @@ -103,7 +119,8 @@ class Method extends ModelElement @override Method? get overriddenElement { - if (_enclosingContainer is Extension) { + if (_enclosingContainer is Extension || + element.enclosingElement is ExtensionElement) { return null; } var parent = element.enclosingElement as InterfaceElement; diff --git a/lib/src/model/model_element.dart b/lib/src/model/model_element.dart index f5495ff52f..690c047798 100644 --- a/lib/src/model/model_element.dart +++ b/lib/src/model/model_element.dart @@ -96,14 +96,15 @@ abstract class ModelElement if (e is PropertyInducingElement) { var elementGetter = e.getter; var getter = elementGetter != null - ? ModelElement.for_(elementGetter, library, p) + ? ModelElement.for_(elementGetter, library, p) as Accessor : null; var elementSetter = e.setter; var setter = elementSetter != null - ? ModelElement.for_(elementSetter, library, p) + ? ModelElement.for_(elementSetter, library, p) as Accessor : null; + return ModelElement.forPropertyInducingElement(e, library, p, - getter: getter as Accessor?, setter: setter as Accessor?); + getter: getter, setter: setter); } return ModelElement.for_(e, library, p); } @@ -166,14 +167,27 @@ abstract class ModelElement getter as ContainerAccessor?, setter as ContainerAccessor?); } } else { - newModelElement = Field.inherited( - e, - enclosingContainer, - library, - packageGraph, - getter as ContainerAccessor?, - setter as ContainerAccessor?, - ); + // Enum fields and extension getters can't be inherited, so this case is + // simpler. + if (e.enclosingElement is ExtensionElement) { + newModelElement = Field.providedByExtension( + e, + enclosingContainer, + library, + packageGraph, + getter as ContainerAccessor?, + setter as ContainerAccessor?, + ); + } else { + newModelElement = Field.inherited( + e, + enclosingContainer, + library, + packageGraph, + getter as ContainerAccessor?, + setter as ContainerAccessor?, + ); + } } } else { throw UnimplementedError( @@ -293,14 +307,24 @@ abstract class ModelElement when e.aliasedType.documentableElement is InterfaceElement => ClassTypedef(e, library, packageGraph), TypeAliasElement() => GeneralizedTypedef(e, library, packageGraph), - MethodElement(isOperator: true) => enclosingContainer == null - ? Operator(e, library, packageGraph) - : Operator.inherited(e, enclosingContainer, library, packageGraph, - originalMember: originalMember), - MethodElement(isOperator: false) => enclosingContainer == null - ? Method(e, library, packageGraph) - : Method.inherited(e, enclosingContainer, library, packageGraph, - originalMember: originalMember as ExecutableMember?), + MethodElement(isOperator: true) when enclosingContainer == null => + Operator(e, library, packageGraph), + MethodElement(isOperator: true) + when e.enclosingElement is ExtensionElement => + Operator.providedByExtension( + e, enclosingContainer, library, packageGraph), + MethodElement(isOperator: true) => Operator.inherited( + e, enclosingContainer, library, packageGraph, + originalMember: originalMember), + MethodElement(isOperator: false) when enclosingContainer == null => + Method(e, library, packageGraph), + MethodElement(isOperator: false) + when e.enclosingElement is ExtensionElement => + Method.providedByExtension( + e, enclosingContainer, library, packageGraph), + MethodElement(isOperator: false) => Method.inherited( + e, enclosingContainer, library, packageGraph, + originalMember: originalMember as ExecutableMember?), ParameterElement() => Parameter(e, library, packageGraph, originalMember: originalMember as ParameterMember?), PropertyAccessorElement() => _constructFromPropertyAccessor( @@ -328,7 +352,7 @@ abstract class ModelElement e.enclosingElement is InterfaceElement || e is MultiplyInheritedExecutableElement) { if (enclosingContainer == null || enclosingContainer is Extension) { - return ContainerAccessor(e, library, packageGraph); + return ContainerAccessor(e, library, packageGraph, enclosingContainer); } return ContainerAccessor.inherited( @@ -691,8 +715,8 @@ abstract class ModelElement Library get library => _library; late final String linkedName = () { - // If we're calling this with an empty name, we probably have the wrong - // element associated with a ModelElement or there's an analysis bug. + // If `name` is empty, we probably have the wrong Element association or + // there's an analyzer issue. assert(name.isNotEmpty || element.kind == ElementKind.DYNAMIC || element.kind == ElementKind.NEVER || diff --git a/lib/src/model/model_function.dart b/lib/src/model/model_function.dart index 733b28f95b..ff3c45df2c 100644 --- a/lib/src/model/model_function.dart +++ b/lib/src/model/model_function.dart @@ -80,4 +80,11 @@ class ModelFunctionTyped extends ModelElement with TypeParameters { Iterable get referenceParents => [library]; late final Callable modelType = getTypeFor(element.type, library) as Callable; + + // For use in templates. + bool get isProvidedByExtension => false; + + // For use in templates. + Extension get enclosingExtension => throw UnsupportedError( + 'Top-level variables are not provided by extensions'); } diff --git a/lib/src/model/operator.dart b/lib/src/model/operator.dart index 0fe9415051..708a8cdc1f 100644 --- a/lib/src/model/operator.dart +++ b/lib/src/model/operator.dart @@ -11,10 +11,22 @@ import 'package:dartdoc/src/model/model.dart'; class Operator extends Method { Operator(super.element, super.library, super.packageGraph); - Operator.inherited(super.element, Container super.enclosingContainer, - super.library, super.packageGraph, - {Member? originalMember}) - : super.inherited(originalMember: originalMember as ExecutableMember?); + Operator.providedByExtension( + super.element, + super.enclosingContainer, + super.library, + super.packageGraph, { + Member? originalMember, + }) : super.providedByExtension( + originalMember: originalMember as ExecutableMember?); + + Operator.inherited( + super.element, + super.enclosingContainer, + super.library, + super.packageGraph, { + Member? originalMember, + }) : super.inherited(originalMember: originalMember as ExecutableMember?); @override String get fullyQualifiedName => diff --git a/lib/src/model/package_graph.dart b/lib/src/model/package_graph.dart index 9a23180882..6a2bd7e156 100644 --- a/lib/src/model/package_graph.dart +++ b/lib/src/model/package_graph.dart @@ -385,7 +385,7 @@ class PackageGraph with CommentReferable, Nameable { hashCode: (InheritingContainer clazz) => clazz.definingContainer.hashCode); - /// A list of extensions that exist in the package graph. + /// The public, documented extensions that exist in the package graph. final Set _extensions = {}; /// Name of the default package. diff --git a/lib/src/model/top_level_variable.dart b/lib/src/model/top_level_variable.dart index 0b577342ce..a6ac996ece 100644 --- a/lib/src/model/top_level_variable.dart +++ b/lib/src/model/top_level_variable.dart @@ -72,6 +72,13 @@ class TopLevelVariable extends ModelElement @override bool get isLate => isFinal && element.isLate; + // For use in templates. + bool get isProvidedByExtension => false; + + // For use in templates. + Extension get enclosingExtension => throw UnsupportedError( + 'Top-level variables are not provided by extensions'); + @override Kind get kind => isConst ? Kind.topLevelConstant : Kind.topLevelProperty; diff --git a/lib/src/render/element_type_renderer.dart b/lib/src/render/element_type_renderer.dart index 9c9ba746c6..0dab39b2d8 100644 --- a/lib/src/render/element_type_renderer.dart +++ b/lib/src/render/element_type_renderer.dart @@ -11,7 +11,7 @@ abstract class ElementTypeRenderer { String renderLinkedName(T elementType); - String renderNameWithGenerics(T elementType) => ''; + String renderNameWithGenerics(T elementType, {bool plain = false}) => ''; String wrapNullabilityParens(T elementType, String inner) => elementType.nullabilitySuffix.isEmpty @@ -43,15 +43,23 @@ abstract class ElementTypeRendererHtml } String _renderNameWithGenerics( - T elementType, String name, Iterable typeArguments) { + T elementType, String name, Iterable typeArguments, + {bool plain = false}) { var buffer = StringBuffer()..write(name); if (typeArguments.isNotEmpty && !typeArguments.every((t) => t.name == 'dynamic')) { - buffer - ..write('<') - ..writeAll(typeArguments.map((t) => t.nameWithGenerics), - ', ') - ..write('>'); + if (plain) { + buffer + ..write('<') + ..writeAll(typeArguments.map((t) => t.nameWithGenericsPlain), ', ') + ..write('>'); + } else { + buffer + ..write('<') + ..writeAll(typeArguments.map((t) => t.nameWithGenerics), + ', ') + ..write('>'); + } } buffer.write(elementType.nullabilitySuffix); return buffer.toString(); @@ -76,15 +84,23 @@ class FunctionTypeElementTypeRendererHtml } @override - String renderNameWithGenerics(FunctionTypeElementType elementType) { + String renderNameWithGenerics(FunctionTypeElementType elementType, + {bool plain = false}) { var buffer = StringBuffer()..write(elementType.name); if (elementType.typeFormals.isNotEmpty) { if (!elementType.typeFormals.every((t) => t.name == 'dynamic')) { - buffer - ..write('<') - ..writeAll(elementType.typeFormals.map((t) => t.name), - ', ') - ..write('>'); + if (plain) { + buffer + ..write('<') + ..writeAll(elementType.typeFormals.map((t) => t.name), ', ') + ..write('>'); + } else { + buffer + ..write('<') + ..writeAll(elementType.typeFormals.map((t) => t.name), + ', ') + ..write('>'); + } } } return buffer.toString(); @@ -104,11 +120,13 @@ class ParameterizedElementTypeRendererHtml ); @override - String renderNameWithGenerics(ParameterizedElementType elementType) => + String renderNameWithGenerics(ParameterizedElementType elementType, + {bool plain = false}) => _renderNameWithGenerics( elementType, elementType.modelElement.name, elementType.typeArguments, + plain: plain, ); } @@ -131,7 +149,8 @@ class RecordElementTypeRendererHtml } @override - String renderNameWithGenerics(RecordElementType elementType) { + String renderNameWithGenerics(RecordElementType elementType, + {bool plain = false}) { return '${elementType.name}${elementType.nullabilitySuffix}'; } } @@ -149,11 +168,13 @@ class AliasedUndefinedElementTypeRendererHtml ); @override - String renderNameWithGenerics(AliasedUndefinedElementType elementType) => + String renderNameWithGenerics(AliasedUndefinedElementType elementType, + {bool plain = false}) => _renderNameWithGenerics( elementType, elementType.aliasElement.name, elementType.aliasArguments, + plain: plain, ); } @@ -169,10 +190,12 @@ class AliasedElementTypeRendererHtml ); @override - String renderNameWithGenerics(AliasedElementType elementType) => + String renderNameWithGenerics(AliasedElementType elementType, + {bool plain = false}) => _renderNameWithGenerics( elementType, elementType.aliasElement.name, elementType.aliasArguments, + plain: plain, ); } diff --git a/lib/templates/_callable.html b/lib/templates/_callable.html index 3474cd1a7d..ef6310e409 100644 --- a/lib/templates/_callable.html +++ b/lib/templates/_callable.html @@ -5,6 +5,14 @@ {{ >categorization }} + {{ #isProvidedByExtension }} + {{ #enclosingExtension }} +

    + Available on {{{ extendedElement.linkedName }}}, + provided by the {{{ linkedName }}} extension +

    + {{ /enclosingExtension }} + {{ /isProvidedByExtension }} {{{ oneLineDoc }}} {{ >attributes }} diff --git a/lib/templates/_instance_fields.html b/lib/templates/_instance_fields.html index 5596f2b331..225c927a1e 100644 --- a/lib/templates/_instance_fields.html +++ b/lib/templates/_instance_fields.html @@ -1,12 +1,12 @@ -{{ #hasPublicInstanceFields }} +{{ #hasAvailableInstanceFields }}

    Properties

    - {{ #publicInstanceFieldsSorted }} + {{ #availableInstanceFieldsSorted }} {{ >property }} - {{ /publicInstanceFieldsSorted }} + {{ /availableInstanceFieldsSorted }}
    -{{ /hasPublicInstanceFields }} \ No newline at end of file +{{ /hasAvailableInstanceFields }} diff --git a/lib/templates/_instance_methods.html b/lib/templates/_instance_methods.html index 7f67723d0c..0d5287c32c 100644 --- a/lib/templates/_instance_methods.html +++ b/lib/templates/_instance_methods.html @@ -1,12 +1,12 @@ -{{ #hasPublicInstanceMethods }} +{{ #hasAvailableInstanceMethods }}

    Methods

    - {{ #publicInstanceMethodsSorted }} + {{ #availableInstanceMethodsSorted }} {{ >callable }} - {{ /publicInstanceMethodsSorted }} + {{ /availableInstanceMethodsSorted }}
    -{{ /hasPublicInstanceMethods }} \ No newline at end of file +{{ /hasAvailableInstanceMethods }} \ No newline at end of file diff --git a/lib/templates/_instance_operators.html b/lib/templates/_instance_operators.html index 6f0061cf3e..426ea8cbc6 100644 --- a/lib/templates/_instance_operators.html +++ b/lib/templates/_instance_operators.html @@ -1,12 +1,12 @@ -{{ #hasPublicInstanceOperators }} +{{ #hasAvailableInstanceOperators }}

    Operators

    - {{ #publicInstanceOperatorsSorted }} + {{ #availableInstanceOperatorsSorted }} {{ >callable }} - {{ /publicInstanceOperatorsSorted }} + {{ /availableInstanceOperatorsSorted }}
    -{{ /hasPublicInstanceOperators }} \ No newline at end of file +{{ /hasAvailableInstanceOperators }} \ No newline at end of file diff --git a/lib/templates/_property.html b/lib/templates/_property.html index 6191e2d581..d4ce1266dd 100644 --- a/lib/templates/_property.html +++ b/lib/templates/_property.html @@ -4,6 +4,14 @@ {{ >categorization }} + {{ #isProvidedByExtension }} + {{ #enclosingExtension }} +

    + Available on {{{ extendedElement.linkedName }}}, + provided by the {{{ linkedName }}} extension +

    + {{ /enclosingExtension }} + {{ /isProvidedByExtension }} {{{ oneLineDoc }}} {{ >attributes }} diff --git a/lib/templates/_sidebar_for_container.html b/lib/templates/_sidebar_for_container.html index 701a87c16b..f446966b66 100644 --- a/lib/templates/_sidebar_for_container.html +++ b/lib/templates/_sidebar_for_container.html @@ -19,52 +19,82 @@ {{!-- Instance members which may or may not be inherited. --}} {{ #isInterface }} - {{ #hasPublicInstanceFields }} + {{ #hasAvailableInstanceFields }}
  • Properties
  • - {{ #publicInstanceFieldsSorted }} - {{{ linkedName }}} - {{ /publicInstanceFieldsSorted }} - {{ /hasPublicInstanceFields }} + {{ #availableInstanceFieldsSorted }} + + {{{ linkedName }}} + {{!-- TODO(srawlins): Look into moving this block into a partial; + currently Mustachio cannot handle multiple partial calls to the + same partial with different types. --}} + {{ #isProvidedByExtension }} + {{ #enclosingExtension }} + (ext) + {{ /enclosingExtension }} + {{ /isProvidedByExtension }} + + {{ /availableInstanceFieldsSorted }} + {{ /hasAvailableInstanceFields }} - {{ #hasPublicInstanceMethods }} + {{ #hasAvailableInstanceMethods }}
  • Methods
  • - {{ #publicInstanceMethodsSorted }} - {{{ linkedName }}} - {{ /publicInstanceMethodsSorted }} - {{ /hasPublicInstanceMethods }} + {{ #availableInstanceMethodsSorted }} + + {{{ linkedName }}} + {{ #isProvidedByExtension }} + {{ #enclosingExtension }} + (ext) + {{ /enclosingExtension }} + {{ /isProvidedByExtension }} + + {{ /availableInstanceMethodsSorted }} + {{ /hasAvailableInstanceMethods }} - {{ #hasPublicInstanceOperators }} + {{ #hasAvailableInstanceOperators }}
  • Operators
  • - {{ #publicInstanceOperatorsSorted }} - {{{ linkedName }}} - {{ /publicInstanceOperatorsSorted }} - {{ /hasPublicInstanceOperators }} + {{ #availableInstanceOperatorsSorted }} + + {{{ linkedName }}} + {{ #isProvidedByExtension }} + {{ #enclosingExtension }} + (ext) + {{ /enclosingExtension }} + {{ /isProvidedByExtension }} + + {{ /availableInstanceOperatorsSorted }} + {{ /hasAvailableInstanceOperators }} {{ /isInterface }} {{!-- Instance members with no inheritance concept. --}} {{ #isExtension }} - {{ #hasPublicInstanceFields }} + {{ #hasAvailableInstanceFields }}
  • Properties
  • - {{ #publicInstanceFieldsSorted }} + {{ #availableInstanceFieldsSorted }}
  • {{{ linkedName }}}
  • - {{ /publicInstanceFieldsSorted }} - {{ /hasPublicInstanceFields }} + {{ /availableInstanceFieldsSorted }} + {{ /hasAvailableInstanceFields }} - {{ #hasPublicInstanceMethods }} + {{ #hasAvailableInstanceMethods }}
  • Methods
  • - {{ #publicInstanceMethodsSorted }} + {{ #availableInstanceMethodsSorted }}
  • {{{ linkedName }}}
  • - {{ /publicInstanceMethodsSorted }} - {{ /hasPublicInstanceMethods }} + {{ /availableInstanceMethodsSorted }} + {{ /hasAvailableInstanceMethods }} - {{ #hasPublicInstanceOperators }} + {{ #hasAvailableInstanceOperators }}
  • Operators
  • - {{ #publicInstanceOperatorsSorted }} + {{ #availableInstanceOperatorsSorted }}
  • {{{ linkedName }}}
  • - {{ /publicInstanceOperatorsSorted }} - {{ /hasPublicInstanceOperators }} + {{ /availableInstanceOperatorsSorted }} + {{ /hasAvailableInstanceOperators }} {{ /isExtension }} {{!-- Static members. --}} diff --git a/lib/templates/extension.html b/lib/templates/extension.html index 8c53713c11..ae101438ee 100644 --- a/lib/templates/extension.html +++ b/lib/templates/extension.html @@ -1,7 +1,7 @@ {{ >head }}
    {{ #self }} diff --git a/test/end2end/model_test.dart b/test/end2end/model_test.dart index b9c7537782..f9c2ce6024 100644 --- a/test/end2end/model_test.dart +++ b/test/end2end/model_test.dart @@ -2094,7 +2094,7 @@ void main() async { test('SpecialList has many inherited methods', () { expect(SpecialList.inheritedMethods.wherePublic, hasLength(49)); - var methods = SpecialList.publicInstanceMethodsSorted + var methods = SpecialList.availableInstanceMethodsSorted .where((m) => m.isInherited) .toList(); expect(methods.first.name, equals('add')); diff --git a/test/src/utils.dart b/test/src/utils.dart index 97dea526a4..99d9a0f1d6 100644 --- a/test/src/utils.dart +++ b/test/src/utils.dart @@ -375,6 +375,15 @@ extension IterableStringExtension on Iterable { reason: 'main content:\n\n${mainContent.join('\n')}', ); } + + /// Verifies that this contains [matchers] in order. + void expectContentContainsAllInOrder(Iterable matchers) { + expect( + this, + containsAllInOrder(matchers), + reason: 'content:\n\n${join('\n')}', + ); + } } extension PackageExtension on Package { diff --git a/test/templates/class_test.dart b/test/templates/class_test.dart index 10a76c2aac..f1644dff5b 100644 --- a/test/templates/class_test.dart +++ b/test/templates/class_test.dart @@ -256,4 +256,142 @@ abstract class C { matches('An instance method.'), ]); } + + void test_instanceMethod_fromExtension() async { + await createPackageWithLibrary(''' +class C {} + +extension E on C { + /// An instance method. + void m() {} +} +'''); + var htmlLines = readLines(['lib', 'C-class.html']); + + htmlLines.expectMainContentContainsAllInOrder([ + matches('

    Methods

    '), + matches('
    '), + matches('m'), + matches('An instance method.'), + ]); + } + + void test_operator_fromExtension() async { + await createPackageWithLibrary(''' +class C {} + +extension E on C { + /// An operator. + int operator +(int other) => 7; +} +'''); + var htmlLines = readLines(['lib', 'C-class.html']); + + htmlLines.expectMainContentContainsAllInOrder([ + matches('

    Operators

    '), + matches('
    '), + matches('operator \\+'), + matches('An operator.'), + ]); + } + + void test_instancePropertyAccessor_fromExtension() async { + await createPackageWithLibrary(''' +class C {} + +extension E on C { + /// An instance getter. + int get f => 1; +} +'''); + var htmlLines = readLines(['lib', 'C-class.html']); + + htmlLines.expectMainContentContainsAllInOrder([ + matches('

    Properties

    '), + matches('
    '), + matches('f'), + matches('An instance getter.'), + ]); + } + + void test_instancePropertyAccessor_fromExtensionOfSupertype() async { + await createPackageWithLibrary(''' +class C {} + +class D extends C {} + +extension E on C { + /// An instance getter. + int get f => 1; +} +'''); + var htmlLines = readLines(['lib', 'D-class.html']); + + htmlLines.expectMainContentContainsAllInOrder([ + matches('

    Properties

    '), + matches('
    '), + matches('f'), + matches('An instance getter.'), + ]); + } + + void test_sidebar_method_providedByExtension() async { + await createPackageWithLibrary(''' +class C {} + +extension E on C { + /// An instance method. + void m() {} +} +'''); + var htmlLines = readLines(['lib', 'C-class-sidebar.html']); + + htmlLines.expectContentContainsAllInOrder([ + matches('Methods'), + matches('m'), + matches('\\(ext\\)'), + ]); + } + + void test_sidebar_operator_providedByExtension() async { + await createPackageWithLibrary(''' +class C {} + +extension E on C { + /// An operator. + int operator +(int other) => 7; +} +'''); + var htmlLines = readLines(['lib', 'C-class-sidebar.html']); + + htmlLines.expectContentContainsAllInOrder([ + matches('Operators'), + matches('operator \\+'), + matches('\\(ext\\)'), + ]); + } + + void test_sidebar_propertyAccessor_providedByExtension() async { + await createPackageWithLibrary(''' +class C {} + +extension E on C { + /// An instance getter. + int get f => 1; +} +'''); + var htmlLines = readLines(['lib', 'C-class-sidebar.html']); + + htmlLines.expectContentContainsAllInOrder([ + matches('Properties'), + matches('f'), + matches('\\(ext\\)'), + ]); + } } diff --git a/test/templates/field_test.dart b/test/templates/field_test.dart index 15519a85af..e23f791054 100644 --- a/test/templates/field_test.dart +++ b/test/templates/field_test.dart @@ -76,6 +76,20 @@ class C { ); } + void test_extension_fieldName() async { + await createPackageWithLibrary(''' +extension E on String { + int f1 = 1; +} +'''); + var f1Lines = readLines(['lib', 'E', 'f1.html']); + f1Lines.expectMainContentContainsAllInOrder( + [ + matches('

    f1 property'), + ], + ); + } + void test_extensionType_representationField_final() async { await createPackageWithLibrary(''' extension type ET(