diff --git a/lib/src/generator/file_structure.dart b/lib/src/generator/file_structure.dart index 03813b6f3d..b8e1776193 100644 --- a/lib/src/generator/file_structure.dart +++ b/lib/src/generator/file_structure.dart @@ -2,15 +2,13 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:dartdoc/src/model/model_element.dart'; +import 'package:dartdoc/src/comment_references/parser.dart'; +import 'package:dartdoc/src/failure.dart'; +import 'package:meta/meta.dart'; -const _html = 'html'; -const _md = 'md'; +import '../model/model.dart'; -enum FileStructureMode { - htmlOriginal, - mdOriginal, -} +const _validFormats = {'html', 'md'}; /// This class defines an interface to allow [ModelElement]s and [Generator]s /// to get information about the desired on-disk representation of a single @@ -20,22 +18,58 @@ enum FileStructureMode { /// to lay them out on disk, and how to link different pages in the structure /// together. abstract class FileStructure { - factory FileStructure(FileStructureMode mode, ModelElement modelElement) { - switch (mode) { - case FileStructureMode.htmlOriginal: - return _FileStructureHtml(modelElement); - case FileStructureMode.mdOriginal: - return _FileStructureMd(modelElement); + factory FileStructure.fromDocumentable(Documentable documentable) { + if (!_validFormats.contains(documentable.config.format)) { + throw DartdocFailure( + 'Internal error: unrecognized config.format: ${documentable.config.format}'); + } + switch (documentable) { + case LibraryContainer(): + // [LibraryContainer]s are not ModelElements, but have documentation. + return FileStructure._fromLibraryContainer(documentable); + case ModelElement(): + // This should be the common case. + return FileStructure._fromModelElement(documentable); + default: + throw UnimplementedError( + 'Tried to build a FileStructure for an unknown subtype of Documentable: ${documentable.runtimeType}'); + } + } + + factory FileStructure._fromLibraryContainer( + LibraryContainer libraryContainer) { + final format = libraryContainer.config.format; + switch (libraryContainer) { + case Category(): + return FileStructureImpl(format, libraryContainer.name, 'topic'); + case Package(): + return FileStructureImpl(format, 'index', null); + default: + throw UnimplementedError( + 'Unrecognized LibraryContainer subtype: ${libraryContainer.runtimeType}'); } } - /// Link to the [ModelElement] the information for this [FileStructure] - /// applies to. - // TODO(jcollins): consider not carrying a reference to a ModelElement and - // calculating necessary bits at construction time. Might be challenging - // if we want to calculate [hasIndependentFile] based on documentation - // length or other variables not always available. - ModelElement get modelElement; + factory FileStructure._fromModelElement(ModelElement modelElement) { + final format = modelElement.config.format; + switch (modelElement) { + case Library(): + return FileStructureImpl(format, modelElement.dirName, 'library'); + case Mixin(): + return FileStructureImpl(format, modelElement.name, 'mixin'); + case Class(): + return FileStructureImpl(format, modelElement.name, 'class'); + case Operator(): + return FileStructureImpl(format, + 'operator_${operatorNames[modelElement.referenceName]}', null); + case GetterSetterCombo(): + return FileStructureImpl(format, modelElement.name, + modelElement.isConst ? 'constant' : null); + default: + return FileStructureImpl( + modelElement.config.format, modelElement.name, null); + } + } /// True if an independent file should be created for this `ModelElement`. bool get hasIndependentFile; @@ -62,50 +96,40 @@ abstract class FileStructure { String get fileType; } -class _FileStructureHtml implements FileStructure { - _FileStructureHtml(this.modelElement); - - @override - // TODO: implement fileName - String get fileName => throw UnimplementedError(); - - @override - String get fileType => _html; - - @override - // TODO: implement hasIndependentFile - bool get hasIndependentFile => throw UnimplementedError(); - - @override - // TODO: implement href - String get href => throw UnimplementedError(); - +@visibleForTesting +class FileStructureImpl implements FileStructure { @override - // TODO: implement htmlId - String get htmlId => throw UnimplementedError(); + final String fileType; - @override - final ModelElement modelElement; + /// This is a name for the underlying [Documentable] that is free of + /// characters that can not appear in a path (URI, Unix, or Windows). + String pathSafeName; - @override - // TODO: implement dirName - String get dirName => throw UnimplementedError(); -} + /// This is a string to disambiguate the filename of the underlying + /// [Documentable] from other files with the same [pathSafeName] in the + /// same directory and is composed with [pathSafeName] to generate [fileName]. + /// It is usually based on [ModelElement.kind], e.g. `'class'`. If null, no + /// disambiguating string will be added. + // TODO(jcollins-g): Legacy layout doesn't always include this; move toward + // always having a disambiguating string. + final String? kindAddition; -class _FileStructureMd implements FileStructure { - _FileStructureMd(this.modelElement); + FileStructureImpl(this.fileType, this.pathSafeName, this.kindAddition); @override - // TODO: implement fileName - String get fileName => throw UnimplementedError(); - @override - // TODO: implement fileType - String get fileType => _md; + /// Initial implementation is bug-for-bug compatible with pre-extraction + /// dartdoc. This means that some types will have kindAdditions, and + /// some will not. See [FileStructure._fromModelElement]. + String get fileName { + if (kindAddition != null) { + return '$pathSafeName-$kindAddition.$fileType'; + } + return '$pathSafeName.$fileType'; + } @override - // TODO: implement hasIndependentFile - bool get hasIndependentFile => throw UnimplementedError(); + bool get hasIndependentFile => true; @override // TODO: implement href @@ -115,9 +139,6 @@ class _FileStructureMd implements FileStructure { // TODO: implement htmlId String get htmlId => throw UnimplementedError(); - @override - final ModelElement modelElement; - @override // TODO: implement dirName String get dirName => throw UnimplementedError(); diff --git a/lib/src/generator/templates.runtime_renderers.dart b/lib/src/generator/templates.runtime_renderers.dart index f5bd477141..9dc55f00cd 100644 --- a/lib/src/generator/templates.runtime_renderers.dart +++ b/lib/src/generator/templates.runtime_renderers.dart @@ -1772,28 +1772,6 @@ class _Renderer_Class extends RendererBase { parent: r, getters: _invisibleGetters['ClassElement']!); }, ), - 'fileName': Property( - getValue: (CT_ c) => c.fileName, - 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.fileName, ast, r.template, sink, - parent: r); - }, - ), 'inheritanceChain': Property( getValue: (CT_ c) => c.inheritanceChain, renderVariable: (CT_ c, Property self, @@ -4042,6 +4020,20 @@ class _Renderer_Documentable extends RendererBase { parent: r); }, ), + 'fileStructure': Property( + getValue: (CT_ c) => c.fileStructure, + renderVariable: (CT_ c, Property self, + List remainingNames) => + self.renderSimpleVariable( + c, remainingNames, 'FileStructure'), + isNullValue: (CT_ c) => false, + renderValue: (CT_ c, RendererBase r, + List ast, StringSink sink) { + renderSimple(c.fileStructure, ast, r.template, sink, + parent: r, + getters: _invisibleGetters['FileStructure']!); + }, + ), 'hasDocumentation': Property( getValue: (CT_ c) => c.hasDocumentation, renderVariable: (CT_ c, Property self, @@ -8351,28 +8343,6 @@ class _Renderer_Library extends RendererBase { _render_Extension(e, ast, r.template, sink, parent: r)); }, ), - 'fileName': Property( - getValue: (CT_ c) => c.fileName, - 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.fileName, ast, r.template, sink, - parent: r); - }, - ), 'filePath': Property( getValue: (CT_ c) => c.filePath, renderVariable: @@ -8815,6 +8785,20 @@ class _Renderer_LibraryContainer extends RendererBase { parent: r); }, ), + 'fileStructure': Property( + getValue: (CT_ c) => c.fileStructure, + renderVariable: (CT_ c, Property self, + List remainingNames) => + self.renderSimpleVariable( + c, remainingNames, 'FileStructure'), + isNullValue: (CT_ c) => false, + renderValue: (CT_ c, RendererBase r, + List ast, StringSink sink) { + renderSimple(c.fileStructure, ast, r.template, sink, + parent: r, + getters: _invisibleGetters['FileStructure']!); + }, + ), 'hasPublicLibraries': Property( getValue: (CT_ c) => c.hasPublicLibraries, renderVariable: (CT_ c, Property self, @@ -8841,19 +8825,6 @@ class _Renderer_LibraryContainer extends RendererBase { _render_Library(e, ast, r.template, sink, parent: r)); }, ), - 'packageGraph': Property( - getValue: (CT_ c) => c.packageGraph, - renderVariable: (CT_ c, Property self, - List remainingNames) => - self.renderSimpleVariable( - c, remainingNames, 'PackageGraph'), - isNullValue: (CT_ c) => false, - renderValue: (CT_ c, RendererBase r, - List ast, StringSink sink) { - renderSimple(c.packageGraph, ast, r.template, sink, - parent: r, getters: _invisibleGetters['PackageGraph']!); - }, - ), 'publicLibraries': Property( getValue: (CT_ c) => c.publicLibraries, renderVariable: (CT_ c, Property self, @@ -10075,28 +10046,6 @@ class _Renderer_Mixin extends RendererBase { parent: r, getters: _invisibleGetters['MixinElement']!); }, ), - 'fileName': Property( - getValue: (CT_ c) => c.fileName, - 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.fileName, ast, r.template, sink, - parent: r); - }, - ), 'hasModifiers': Property( getValue: (CT_ c) => c.hasModifiers, renderVariable: (CT_ c, Property self, @@ -10755,6 +10704,20 @@ class _Renderer_ModelElement extends RendererBase { parent: r); }, ), + 'fileStructure': Property( + getValue: (CT_ c) => c.fileStructure, + renderVariable: (CT_ c, Property self, + List remainingNames) => + self.renderSimpleVariable( + c, remainingNames, 'FileStructure'), + isNullValue: (CT_ c) => false, + renderValue: (CT_ c, RendererBase r, + List ast, StringSink sink) { + renderSimple(c.fileStructure, ast, r.template, sink, + parent: r, + getters: _invisibleGetters['FileStructure']!); + }, + ), 'fileType': Property( getValue: (CT_ c) => c.fileType, renderVariable: @@ -11956,28 +11919,6 @@ class _Renderer_Operator extends RendererBase { CT_, () => { ..._Renderer_Method.propertyMap(), - 'fileName': Property( - getValue: (CT_ c) => c.fileName, - 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.fileName, ast, r.template, sink, - parent: r); - }, - ), 'fullyQualifiedName': Property( getValue: (CT_ c) => c.fullyQualifiedName, renderVariable: @@ -13109,13 +13050,13 @@ class _Renderer_PackageTemplateData extends RendererBase { } } -String renderSearchPage(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(); @@ -15380,28 +15321,6 @@ class _Renderer_TopLevelVariable extends RendererBase { parent: r, getters: _invisibleGetters['Feature']!)); }, ), - 'fileName': Property( - getValue: (CT_ c) => c.fileName, - 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.fileName, ast, r.template, sink, - parent: r); - }, - ), 'filePath': Property( getValue: (CT_ c) => c.filePath, renderVariable: @@ -17096,6 +17015,16 @@ const _invisibleGetters = { 'modificationStamp', 'runtimeType' }, + 'FileStructure': { + 'dirName', + 'fileName', + 'fileType', + 'hasIndependentFile', + 'hashCode', + 'href', + 'htmlId', + 'runtimeType' + }, 'FunctionElement': { 'hashCode', 'isDartCoreIdentical', diff --git a/lib/src/model/category.dart b/lib/src/model/category.dart index c5cf534f70..232c6f74c1 100644 --- a/lib/src/model/category.dart +++ b/lib/src/model/category.dart @@ -92,9 +92,7 @@ class Category extends Nameable @override String get fullyQualifiedName => name; - String get _fileType => package.fileType; - - String get filePath => 'topics/$name-topic.$_fileType'; + String get filePath => 'topics/${fileStructure.fileName}'; @override String? get href => isCanonical ? '${package.baseHref}$filePath' : null; @@ -157,10 +155,8 @@ class Category extends Nameable packageGraph.rendererFactory.categoryRenderer; @override - // TODO: implement referenceChildren Map get referenceChildren => const {}; @override - // TODO: implement referenceParents Iterable get referenceParents => const []; } diff --git a/lib/src/model/class.dart b/lib/src/model/class.dart index 77da3fcba6..b4599731e3 100644 --- a/lib/src/model/class.dart +++ b/lib/src/model/class.dart @@ -24,7 +24,8 @@ class Class extends InheritingContainer ]; @override - String get sidebarPath => '${library.dirName}/$name-class-sidebar.$fileType'; + String get sidebarPath => + '${library.dirName}/$name-class-sidebar.${fileStructure.fileType}'; @override late final List inheritanceChain = [ @@ -47,9 +48,6 @@ class Class extends InheritingContainer packageGraph.specialClasses.addSpecial(this); } - @override - String get fileName => '$name-class.$fileType'; - @override bool get isAbstract => element.isAbstract; diff --git a/lib/src/model/constructor.dart b/lib/src/model/constructor.dart index ab3e4d21ae..cba39df45b 100644 --- a/lib/src/model/constructor.dart +++ b/lib/src/model/constructor.dart @@ -37,7 +37,7 @@ class Constructor extends ModelElement @override String get filePath => - '${enclosingElement.library.dirName}/${enclosingElement.name}/$fileName'; + '${enclosingElement.library.dirName}/${enclosingElement.name}/${fileStructure.fileName}'; @override String get aboveSidebarPath => enclosingElement.sidebarPath; diff --git a/lib/src/model/container.dart b/lib/src/model/container.dart index 3202a218fa..4715e15ecb 100644 --- a/lib/src/model/container.dart +++ b/lib/src/model/container.dart @@ -266,7 +266,7 @@ abstract class Container extends ModelElement Iterable get referenceParents => [definingLibrary, library]; @override - String get filePath => '${library.dirName}/$fileName'; + String get filePath => '${library.dirName}/${fileStructure.fileName}'; /// The full path of this element's sidebar file. String get sidebarPath; diff --git a/lib/src/model/documentable.dart b/lib/src/model/documentable.dart index 7075af566f..f7ba55375c 100644 --- a/lib/src/model/documentable.dart +++ b/lib/src/model/documentable.dart @@ -4,6 +4,7 @@ import 'package:analyzer/file_system/file_system.dart'; import 'package:dartdoc/src/dartdoc_options.dart'; +import 'package:dartdoc/src/generator/file_structure.dart'; import 'package:dartdoc/src/io_utils.dart'; import 'model.dart'; @@ -25,6 +26,8 @@ abstract class Documentable extends Nameable { DartdocOptionContext get config; + FileStructure get fileStructure; + String? get href; String get kind; diff --git a/lib/src/model/enum.dart b/lib/src/model/enum.dart index b3a4365a71..4566e8bdbf 100644 --- a/lib/src/model/enum.dart +++ b/lib/src/model/enum.dart @@ -35,7 +35,8 @@ class Enum extends InheritingContainer ]; @override - String get sidebarPath => '${library.dirName}/$name-enum-sidebar.$fileType'; + String get sidebarPath => + '${library.dirName}/$name-enum-sidebar.${fileStructure.fileType}'; @override String get kind => 'enum'; @@ -116,7 +117,9 @@ class EnumField extends Field { assert(!(canonicalLibrary == null || canonicalEnclosingContainer == null)); assert(canonicalLibrary == library); assert(canonicalEnclosingContainer == enclosingElement); - return '${package.baseHref}${enclosingElement.library.dirName}/${enclosingElement.fileName}'; + // TODO(jcollins-g): EnumField should not depend on enclosingElement, but + // we sort of have to while we are half-converted to [FileStructure]. + return '${package.baseHref}${enclosingElement.library.dirName}/${enclosingElement.fileStructure.fileName}'; } @override diff --git a/lib/src/model/extension.dart b/lib/src/model/extension.dart index 0cddced1ca..fa1c8da0b1 100644 --- a/lib/src/model/extension.dart +++ b/lib/src/model/extension.dart @@ -93,11 +93,11 @@ class Extension extends Container implements EnclosedElement { ]; @override - String get filePath => '${library.dirName}/$fileName'; + String get filePath => '${library.dirName}/${fileStructure.fileName}'; @override String get sidebarPath => - '${library.dirName}/$name-extension-sidebar.$fileType'; + '${library.dirName}/$name-extension-sidebar.${fileStructure.fileType}'; Map? _referenceChildren; @override diff --git a/lib/src/model/field.dart b/lib/src/model/field.dart index d1f5edb8ce..ce19220f53 100644 --- a/lib/src/model/field.dart +++ b/lib/src/model/field.dart @@ -71,7 +71,7 @@ class Field extends ModelElement @override String get filePath => - '${enclosingElement.library.dirName}/${enclosingElement.name}/$fileName'; + '${enclosingElement.library.dirName}/${enclosingElement.name}/${fileStructure.fileName}'; @override String? get href { @@ -144,7 +144,8 @@ class Field extends ModelElement FieldElement? get field => element; @override - String get fileName => '${isConst ? '$name-constant' : name}.$fileType'; + String get fileName => + '${isConst ? '$name-constant' : name}.${fileStructure.fileType}'; @override String get aboveSidebarPath => enclosingElement.sidebarPath; diff --git a/lib/src/model/library.dart b/lib/src/model/library.dart index e3da6f78c3..748276ed66 100644 --- a/lib/src/model/library.dart +++ b/lib/src/model/library.dart @@ -331,13 +331,10 @@ class Library extends ModelElement allClasses.where((c) => c.isErrorOrException).toList(growable: false); @override - String get fileName => '$dirName-library.$fileType'; - - @override - String get filePath => '${library.dirName}/$fileName'; + String get filePath => '${library.dirName}/${fileStructure.fileName}'; String get sidebarPath => - '${library.dirName}/$dirName-library-sidebar.$fileType'; + '${library.dirName}/$dirName-library-sidebar.${fileStructure.fileType}'; /// The library template manually includes 'packages' in the left/above /// sidebar. diff --git a/lib/src/model/library_container.dart b/lib/src/model/library_container.dart index 64aa1e5306..3dc6758c52 100644 --- a/lib/src/model/library_container.dart +++ b/lib/src/model/library_container.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import 'package:collection/collection.dart'; +import 'package:dartdoc/src/generator/file_structure.dart'; import 'package:dartdoc/src/model/model.dart'; import 'package:dartdoc/src/model_utils.dart' as model_utils; @@ -11,11 +12,9 @@ import 'package:dartdoc/src/model_utils.dart' as model_utils; /// Do not cache return values of any methods or members excepting [libraries] /// and [name] before finishing initialization of a [LibraryContainer]. abstract mixin class LibraryContainer - implements Nameable, Comparable { + implements Nameable, Comparable, Documentable { final List libraries = []; - PackageGraph get packageGraph; - Iterable get publicLibraries => model_utils.filterNonPublic(libraries); @@ -64,4 +63,7 @@ abstract mixin class LibraryContainer } return Comparable.compare(_group, other._group); } + + @override + late final FileStructure fileStructure = FileStructure.fromDocumentable(this); } diff --git a/lib/src/model/method.dart b/lib/src/model/method.dart index 4e78ca836e..c7916a7f05 100644 --- a/lib/src/model/method.dart +++ b/lib/src/model/method.dart @@ -62,7 +62,7 @@ class Method extends ModelElement @override String get filePath => - '${enclosingElement.library.dirName}/${enclosingElement.name}/$fileName'; + '${enclosingElement.library.dirName}/${enclosingElement.name}/${fileStructure.fileName}'; @override String get aboveSidebarPath => enclosingElement.sidebarPath; diff --git a/lib/src/model/mixin.dart b/lib/src/model/mixin.dart index c7f18db23f..954aa98652 100644 --- a/lib/src/model/mixin.dart +++ b/lib/src/model/mixin.dart @@ -24,7 +24,8 @@ class Mixin extends InheritingContainer with TypeImplementing { ]; @override - String get sidebarPath => '${library.dirName}/$name-mixin-sidebar.$fileType'; + String get sidebarPath => + '${library.dirName}/$name-mixin-sidebar.${fileStructure.fileType}'; @override late final List inheritanceChain = [ @@ -48,9 +49,6 @@ class Mixin extends InheritingContainer with TypeImplementing { Iterable> get extraReferenceChildren => const []; - @override - String get fileName => '$name-mixin.$fileType'; - @override bool get hasModifiers => super.hasModifiers || hasPublicSuperclassConstraints; diff --git a/lib/src/model/model_element.dart b/lib/src/model/model_element.dart index 320017b985..987b3dca8f 100644 --- a/lib/src/model/model_element.dart +++ b/lib/src/model/model_element.dart @@ -15,6 +15,7 @@ import 'package:analyzer/src/dart/element/member.dart' show ExecutableMember, Member, ParameterMember; import 'package:collection/collection.dart'; import 'package:dartdoc/src/dartdoc_options.dart'; +import 'package:dartdoc/src/generator/file_structure.dart'; import 'package:dartdoc/src/model/annotation.dart'; import 'package:dartdoc/src/model/comment_referable.dart'; import 'package:dartdoc/src/model/feature.dart'; @@ -641,9 +642,11 @@ abstract class ModelElement extends Canonicalization /// The name of the output file in which this element will be primarily /// documented. - String get fileName => '$name.$fileType'; + @Deprecated('replace with fileStructure.fileName') + String get fileName => fileStructure.fileName; - String get fileType => package.fileType; + @Deprecated('replace with fileStructure.fileType') + String get fileType => fileStructure.fileType; /// The full path of the output file in which this element will be primarily /// documented. @@ -916,4 +919,7 @@ abstract class ModelElement extends Canonicalization } String get linkedObjectType => _packageGraph.dartCoreObject; + + @override + late final FileStructure fileStructure = FileStructure.fromDocumentable(this); } diff --git a/lib/src/model/model_function.dart b/lib/src/model/model_function.dart index 300f7e7eb3..78734593de 100644 --- a/lib/src/model/model_function.dart +++ b/lib/src/model/model_function.dart @@ -52,7 +52,7 @@ class ModelFunctionTyped extends ModelElement Library get enclosingElement => library; @override - String get filePath => '${library.dirName}/$fileName'; + String get filePath => '${library.dirName}/${fileStructure.fileName}'; @override String get aboveSidebarPath => enclosingElement.sidebarPath; diff --git a/lib/src/model/operator.dart b/lib/src/model/operator.dart index f2b438df35..0fb87ea956 100644 --- a/lib/src/model/operator.dart +++ b/lib/src/model/operator.dart @@ -5,7 +5,6 @@ // ignore: implementation_imports import 'package:analyzer/src/dart/element/member.dart' show ExecutableMember, Member; -import 'package:dartdoc/src/comment_references/parser.dart'; import 'package:dartdoc/src/model/model.dart'; class Operator extends Method { @@ -16,15 +15,6 @@ class Operator extends Method { {Member? originalMember}) : super.inherited(originalMember: originalMember as ExecutableMember?); - @override - String get fileName { - var actualName = super.name; - if (operatorNames.containsKey(actualName)) { - actualName = 'operator_${operatorNames[actualName]}'; - } - return '$actualName.$fileType'; - } - @override String get fullyQualifiedName => '${library.name}.${enclosingElement.name}.${super.name}'; diff --git a/lib/src/model/top_level_variable.dart b/lib/src/model/top_level_variable.dart index 7fde0ceefd..a24398090b 100644 --- a/lib/src/model/top_level_variable.dart +++ b/lib/src/model/top_level_variable.dart @@ -43,7 +43,7 @@ class TopLevelVariable extends ModelElement Library get enclosingElement => library; @override - String get filePath => '${library.dirName}/$fileName'; + String get filePath => '${library.dirName}/${fileStructure.fileName}'; @override String get aboveSidebarPath => enclosingElement.sidebarPath; @@ -81,9 +81,6 @@ class TopLevelVariable extends ModelElement @override Set get features => {...super.features, ...comboFeatures}; - @override - String get fileName => '${isConst ? '$name-constant' : name}.$fileType'; - @override Iterable get referenceParents => [definingLibrary]; diff --git a/lib/src/model/typedef.dart b/lib/src/model/typedef.dart index 73a4153b53..2f80d4235a 100644 --- a/lib/src/model/typedef.dart +++ b/lib/src/model/typedef.dart @@ -36,7 +36,7 @@ abstract class Typedef extends ModelElement _renderer.renderLinkedGenericParameters(this); @override - String get filePath => '${library.dirName}/$fileName'; + String get filePath => '${library.dirName}/${fileStructure.fileName}'; @override String get aboveSidebarPath => enclosingElement.sidebarPath; diff --git a/test/dartdoc_test_base.dart b/test/dartdoc_test_base.dart index aa18c6eaad..a660d051ee 100644 --- a/test/dartdoc_test_base.dart +++ b/test/dartdoc_test_base.dart @@ -70,23 +70,40 @@ analyzer: packagePath, name, Uri.file('$packagePath/')); } + Future _bootPackageFromFiles( + Iterable files) async { + var packagePathBasename = + resourceProvider.pathContext.basename(packagePath); + var packagePathDirname = resourceProvider.pathContext.dirname(packagePath); + await d + .dir(packagePathBasename, files) + .createInMemory(resourceProvider, packagePathDirname); + return await bootBasicPackage( + packagePath, + packageMetaProvider, + packageConfigProvider, + ); + } + + /// Creates a single library named [libraryName], with optional preamble + /// [libraryPreamble]. Optionally, pass a [FileGenerator] to create + /// extra files in the package such as `dartdoc_options.yaml`. Future bootPackageWithLibrary(String libraryContent, - {String libraryPreamble = ''}) async { - await d.dir('lib', [ - d.file('lib.dart', ''' + {String libraryPreamble = '', + Iterable extraFiles = const []}) async { + return (await _bootPackageFromFiles([ + d.dir('lib', [ + d.file('lib.dart', ''' $libraryPreamble library $libraryName; $libraryContent '''), - ]).createInMemory(resourceProvider, packagePath); - - var packageGraph = await bootBasicPackage( - packagePath, - packageMetaProvider, - packageConfigProvider, - ); - return packageGraph.libraries.named(libraryName); + ]), + ...extraFiles + ])) + .libraries + .named(libraryName); } /// Similar to [bootPackageWithLibrary], but allows for more complex @@ -107,43 +124,33 @@ $libraryContent List show = const [], List hide = const []}) async { final subdir = reexportPrivate ? 'src' : 'subdir'; - await d.dir('lib', [ - d.dir(subdir, [ - d.file('lib.dart', ''' -library ${libraryName}_src; - -$reexportedContent -'''), - ]) - ]).createInMemory(resourceProvider, packagePath); - if (show.isNotEmpty && hide.isNotEmpty) { throw DartdocTestBaseFailure('Can not specify show and hide'); } - final showHideString = '${show.isNotEmpty ? 'show ${show.join(', ')}' : ''}' '${hide.isNotEmpty ? 'hide ${hide.join(', ')}' : ''}'; - await d.dir('lib', [ - d.file('lib.dart', ''' + + return (await _bootPackageFromFiles([ + d.dir('lib', [ + d.dir(subdir, [ + d.file('lib.dart', ''' +library ${libraryName}_src; + +$reexportedContent +'''), + ]), + d.file('lib.dart', ''' library ${libraryName}_lib; export '$subdir/lib.dart' $showHideString; '''), - ]).createInMemory(resourceProvider, packagePath); - - await d.dir('lib', [ - d.file('importing_lib.dart', ''' + d.file('importing_lib.dart', ''' library $libraryName; - $libraryContent '''), - ]).createInMemory(resourceProvider, packagePath); - - var packageGraph = await bootBasicPackage( - packagePath, - packageMetaProvider, - packageConfigProvider, - ); - return packageGraph.libraries.named(libraryName); + ]) + ])) + .libraries + .named(libraryName); } } diff --git a/test/end2end/model_test.dart b/test/end2end/model_test.dart index 2e27dd69be..d10a3ceff7 100644 --- a/test/end2end/model_test.dart +++ b/test/end2end/model_test.dart @@ -11,6 +11,7 @@ import 'package:analyzer/dart/element/type.dart'; import 'package:analyzer/source/line_info.dart'; import 'package:async/async.dart'; import 'package:collection/src/iterable_extensions.dart'; +import 'package:dartdoc/src/dartdoc_options.dart'; import 'package:dartdoc/src/element_type.dart'; import 'package:dartdoc/src/matching_link_result.dart'; import 'package:dartdoc/src/model/feature.dart'; @@ -51,6 +52,30 @@ class TestLibraryContainer extends LibraryContainer with Nameable { TestLibraryContainer( this.name, this.containerOrder, LibraryContainer? enclosingContainer) : enclosingName = enclosingContainer?.name ?? ''; + + @override + DartdocOptionContext get config => throw UnimplementedError(); + + @override + String? get documentation => throw UnimplementedError(); + + @override + String get documentationAsHtml => throw UnimplementedError(); + + @override + bool get hasDocumentation => throw UnimplementedError(); + + @override + String? get href => throw UnimplementedError(); + + @override + bool get isDocumented => throw UnimplementedError(); + + @override + String get kind => throw UnimplementedError(); + + @override + String get oneLineDoc => throw UnimplementedError(); } class TestLibraryContainerSdk extends TestLibraryContainer { diff --git a/test/generator/file_structure_test.dart b/test/generator/file_structure_test.dart index 9e2463f512..e79b833743 100644 --- a/test/generator/file_structure_test.dart +++ b/test/generator/file_structure_test.dart @@ -2,11 +2,11 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:dartdoc/src/generator/file_structure.dart'; import 'package:test/test.dart'; import 'package:test_reflective_loader/test_reflective_loader.dart'; import '../dartdoc_test_base.dart'; +import '../src/test_descriptor_utils.dart' as d; import '../src/utils.dart'; void main() { @@ -20,15 +20,55 @@ class FileStructureTest extends DartdocTestBase { @override String get libraryName => 'file_structure_test'; - void test_createFileStructureForVariable() async { + void test_fileNamesForModelElements() async { var library = await bootPackageWithLibrary(''' var globalVar = 123; -'''); +const globalConst = 123; + +class AClass { + const myConst = 75; + var myVariable = 76; + + void aMethod(); + + @override + operator+ (AClass b) => AClass(); +} + +mixin BMixin on AClass {} +''', libraryPreamble: ''' +/// {@category MyCategory} +''', extraFiles: [ + d.file('dartdoc_options.yaml', ''' +dartdoc: + categories: + "MyCategory": + markdown: mycategory.md +'''), + d.file('mycategory.md', ''' +Hello there, I am an *amazing* markdown file. +'''), + ]); var globalVar = library.properties.named('globalVar'); - var htmlStructure = - FileStructure(FileStructureMode.htmlOriginal, globalVar); - var mdStructure = FileStructure(FileStructureMode.mdOriginal, globalVar); - expect(htmlStructure.fileType, equals('html')); - expect(mdStructure.fileType, equals('md')); + var globalConst = library.constants.named('globalConst'); + var category = + library.package.categories.firstWhere((c) => c.name == 'MyCategory'); + var AClass = library.classes.named('AClass'); + var BMixin = library.mixins.named('BMixin'); + var myConst = AClass.constantFields.named('myConst'); + var myVariable = AClass.instanceFields.named('myVariable'); + var aMethod = AClass.instanceMethods.named('aMethod'); + var operatorPlus = AClass.instanceOperators.named('operator +'); + + expect(globalVar.fileStructure.fileName, equals('globalVar.html')); + expect(globalConst.fileStructure.fileName, + equals('globalConst-constant.html')); + expect(category.fileStructure.fileName, equals('MyCategory-topic.html')); + expect(AClass.fileStructure.fileName, equals('AClass-class.html')); + expect(BMixin.fileStructure.fileName, equals('BMixin-mixin.html')); + expect(myConst.fileStructure.fileName, equals('myConst-constant.html')); + expect(myVariable.fileStructure.fileName, equals('myVariable.html')); + expect(aMethod.fileStructure.fileName, equals('aMethod.html')); + expect(operatorPlus.fileStructure.fileName, equals('operator_plus.html')); } } diff --git a/tool/grind.dart b/tool/grind.dart index 7b89cf3a82..f26f2308b6 100644 --- a/tool/grind.dart +++ b/tool/grind.dart @@ -72,11 +72,11 @@ Future get cleanFlutterRepo async { if (repoCompleter != null) { return repoCompleter.future; } - _cleanFlutterRepo = repoCompleter; // No await is allowed between check of _cleanFlutterRepo and its assignment, // to prevent reentering this function. repoCompleter = Completer(); + _cleanFlutterRepo = repoCompleter; // Figure out where the repository is supposed to be and lock updates for // it.