diff --git a/lib/src/generator/templates.aot_renderers_for_html.dart b/lib/src/generator/templates.aot_renderers_for_html.dart index 16336b15a8..bced76cacc 100644 --- a/lib/src/generator/templates.aot_renderers_for_html.dart +++ b/lib/src/generator/templates.aot_renderers_for_html.dart @@ -16,7 +16,6 @@ import 'dart:convert'; import 'package:dartdoc/src/generator/template_data.dart'; import 'package:dartdoc/src/model/accessor.dart'; -import 'package:dartdoc/src/model/canonicalization.dart'; import 'package:dartdoc/src/model/category.dart'; import 'package:dartdoc/src/model/class.dart'; import 'package:dartdoc/src/model/constructor.dart'; @@ -37,6 +36,7 @@ import 'package:dartdoc/src/model/operator.dart'; import 'package:dartdoc/src/model/package.dart'; import 'package:dartdoc/src/model/top_level_variable.dart'; import 'package:dartdoc/src/model/typedef.dart'; +import 'package:dartdoc/src/warnings.dart'; String renderCategory(CategoryTemplateData context0) { final buffer = StringBuffer(); @@ -3524,8 +3524,7 @@ String _deduplicated_lib_templates__head_html(TemplateDataBase context0) { return buffer.toString(); } -String _deduplicated_lib_templates__documentation_html( - Canonicalization context0) { +String _deduplicated_lib_templates__documentation_html(Warnable context0) { final buffer = StringBuffer(); if (context0.hasDocumentation) { buffer.writeln(); diff --git a/lib/src/generator/templates.runtime_renderers.dart b/lib/src/generator/templates.runtime_renderers.dart index f1261a1e4c..0a32007b29 100644 --- a/lib/src/generator/templates.runtime_renderers.dart +++ b/lib/src/generator/templates.runtime_renderers.dart @@ -310,28 +310,6 @@ class _Renderer_Accessor extends RendererBase { parent: r); }, ), - 'namePart': Property( - getValue: (CT_ c) => c.namePart, - 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.namePart, ast, r.template, sink, - parent: r); - }, - ), 'originalMember': Property( getValue: (CT_ c) => c.originalMember, renderVariable: (CT_ c, Property self, @@ -761,49 +739,6 @@ class _Renderer_CanonicalFor extends RendererBase { } } -class _Renderer_Canonicalization extends RendererBase { - static final Map _propertyMapCache = {}; - static Map> propertyMap< - CT_ extends Canonicalization>() => - _propertyMapCache.putIfAbsent( - CT_, - () => { - ..._Renderer_Object.propertyMap(), - 'isCanonical': Property( - getValue: (CT_ c) => c.isCanonical, - renderVariable: (CT_ c, Property self, - List remainingNames) => - self.renderSimpleVariable(c, remainingNames, 'bool'), - getBool: (CT_ c) => c.isCanonical, - ), - 'locationPieces': Property( - getValue: (CT_ c) => c.locationPieces, - renderVariable: (CT_ c, Property self, - List remainingNames) => - self.renderSimpleVariable( - c, remainingNames, 'Set'), - renderIterable: (CT_ c, RendererBase r, - List ast, StringSink sink) { - return c.locationPieces.map((e) => - _render_String(e, ast, r.template, sink, parent: r)); - }, - ), - }) as Map>; - - _Renderer_Canonicalization(Canonicalization context, - RendererBase? parent, Template template, StringSink sink) - : super(context, parent, template, sink); - - @override - Property? getProperty(String key) { - if (propertyMap().containsKey(key)) { - return propertyMap()[key]; - } else { - return null; - } - } -} - class _Renderer_Categorization extends RendererBase { static final Map _propertyMapCache = {}; static Map> propertyMap() => @@ -903,7 +838,6 @@ class _Renderer_Category extends RendererBase { ..._Renderer_Warnable.propertyMap(), ..._Renderer_CommentReferable.propertyMap(), ..._Renderer_Locatable.propertyMap(), - ..._Renderer_Canonicalization.propertyMap(), ..._Renderer_MarkdownFileDocumentation.propertyMap(), ..._Renderer_LibraryContainer.propertyMap(), ..._Renderer_TopLevelContainer.propertyMap(), @@ -9028,6 +8962,13 @@ class _Renderer_Locatable extends RendererBase { _render_String(c.href!, ast, r.template, sink, parent: r); }, ), + 'isCanonical': Property( + getValue: (CT_ c) => c.isCanonical, + renderVariable: (CT_ c, Property self, + List remainingNames) => + self.renderSimpleVariable(c, remainingNames, 'bool'), + getBool: (CT_ c) => c.isCanonical, + ), 'location': Property( getValue: (CT_ c) => c.location, renderVariable: @@ -9180,18 +9121,6 @@ class _Renderer_MarkdownFileDocumentation parent: r); }, ), - 'locationPieces': Property( - getValue: (CT_ c) => c.locationPieces, - renderVariable: (CT_ c, Property self, - List remainingNames) => - self.renderSimpleVariable( - c, remainingNames, 'Set'), - renderIterable: (CT_ c, RendererBase r, - List ast, StringSink sink) { - return c.locationPieces.map((e) => - _render_String(e, ast, r.template, sink, parent: r)); - }, - ), 'oneLineDoc': Property( getValue: (CT_ c) => c.oneLineDoc, renderVariable: @@ -10146,7 +10075,7 @@ class _Renderer_ModelElement extends RendererBase { _propertyMapCache.putIfAbsent( CT_, () => { - ..._Renderer_Canonicalization.propertyMap(), + ..._Renderer_Object.propertyMap(), ..._Renderer_CommentReferable.propertyMap(), ..._Renderer_Warnable.propertyMap(), ..._Renderer_Locatable.propertyMap(), @@ -10792,18 +10721,6 @@ class _Renderer_ModelElement extends RendererBase { parent: r); }, ), - 'locationPieces': Property( - getValue: (CT_ c) => c.locationPieces, - renderVariable: (CT_ c, Property self, - List remainingNames) => - self.renderSimpleVariable( - c, remainingNames, 'Set'), - renderIterable: (CT_ c, RendererBase r, - List ast, StringSink sink) { - return c.locationPieces.map((e) => - _render_String(e, ast, r.template, sink, parent: r)); - }, - ), 'modelNode': Property( getValue: (CT_ c) => c.modelNode, renderVariable: (CT_ c, Property self, @@ -11428,40 +11345,6 @@ class _Renderer_Nameable extends RendererBase { _render_String(c.name, ast, r.template, sink, parent: r); }, ), - 'namePart': Property( - getValue: (CT_ c) => c.namePart, - 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.namePart, ast, r.template, sink, - parent: r); - }, - ), - 'namePieces': Property( - getValue: (CT_ c) => c.namePieces, - renderVariable: (CT_ c, Property self, - List remainingNames) => - self.renderSimpleVariable( - c, remainingNames, 'Set'), - renderIterable: (CT_ c, RendererBase r, - List ast, StringSink sink) { - return c.namePieces.map((e) => - _render_String(e, ast, r.template, sink, parent: r)); - }, - ), 'packageGraph': Property( getValue: (CT_ c) => c.packageGraph, renderVariable: (CT_ c, Property self, @@ -11711,7 +11594,6 @@ class _Renderer_Package extends RendererBase { ..._Renderer_LibraryContainer.propertyMap(), ..._Renderer_Nameable.propertyMap(), ..._Renderer_Locatable.propertyMap(), - ..._Renderer_Canonicalization.propertyMap(), ..._Renderer_Warnable.propertyMap(), ..._Renderer_CommentReferable.propertyMap(), 'aboveSidebarPath': Property( @@ -12172,18 +12054,6 @@ class _Renderer_Package extends RendererBase { parent: r); }, ), - 'locationPieces': Property( - getValue: (CT_ c) => c.locationPieces, - renderVariable: (CT_ c, Property self, - List remainingNames) => - self.renderSimpleVariable( - c, remainingNames, 'Set'), - renderIterable: (CT_ c, RendererBase r, - List ast, StringSink sink) { - return c.locationPieces.map((e) => - _render_String(e, ast, r.template, sink, parent: r)); - }, - ), 'name': Property( getValue: (CT_ c) => c.name, renderVariable: @@ -16397,6 +16267,7 @@ const _invisibleGetters = { 'documentationIsLocal', 'fullyQualifiedName', 'href', + 'isCanonical', 'location' }, 'Map': { diff --git a/lib/src/model/accessor.dart b/lib/src/model/accessor.dart index 900a75358e..7e42a5dda1 100644 --- a/lib/src/model/accessor.dart +++ b/lib/src/model/accessor.dart @@ -156,22 +156,15 @@ class Accessor extends ModelElement { @override Kind get kind => Kind.accessor; - String get _namePart => super.namePart.split('=').first; - - @override - String get namePart => _namePart; - - @override - /// Accessors should never be participating directly in comment reference /// lookups. + @override Map get referenceChildren => enclosingCombo.referenceChildren; - @override - /// Accessors should never be participating directly in comment reference /// lookups. + @override Iterable get referenceParents => enclosingCombo.referenceParents; } diff --git a/lib/src/model/canonicalization.dart b/lib/src/model/canonicalization.dart index 1f9087a13e..61121ca229 100644 --- a/lib/src/model/canonicalization.dart +++ b/lib/src/model/canonicalization.dart @@ -10,17 +10,20 @@ import 'package:dartdoc/src/warnings.dart'; /// This provides heuristic scoring to determine which library a human likely /// considers this element to be primarily 'from', and therefore, canonical. /// Still warn if the heuristic isn't very confident. -abstract mixin class Canonicalization - implements Locatable, Documentable, Warnable { - bool get isCanonical; +final class Canonicalization { + final ModelElement _element; - /// Pieces of the location, split to remove 'package:' and slashes. - Set get locationPieces; + Canonicalization(this._element); Library calculateCanonicalCandidate(Iterable libraries) { + var locationPieces = _element.element.location + .toString() + .split(_locationSplitter) + .where((s) => s.isNotEmpty) + .toSet(); var scoredCandidates = libraries .map((library) => _scoreElementWithLibrary( - library, fullyQualifiedName, locationPieces)) + library, _element.fullyQualifiedName, locationPieces)) .toList(growable: false) ..sort(); @@ -31,11 +34,11 @@ abstract mixin class Canonicalization var confidence = highestScore - secondHighestScore; final canonicalLibrary = librariesByScore.last; - if (confidence < config.ambiguousReexportScorerMinConfidence) { + if (confidence < _element.config.ambiguousReexportScorerMinConfidence) { var libraryNames = librariesByScore.map((l) => l.name); var message = '$libraryNames -> ${canonicalLibrary.name} ' '(confidence ${confidence.toStringAsPrecision(4)})'; - warn(PackageWarning.ambiguousReexport, + _element.warn(PackageWarning.ambiguousReexport, message: message, extendedDebug: scoredCandidates.map((s) => '$s')); } @@ -61,17 +64,19 @@ abstract mixin class Canonicalization scoredCandidate._alterScore(-1.0, _Reason.deprecated); } + var libraryNamePieces = { + ...library.name.split('.').where((s) => s.isNotEmpty) + }; + // Give a big boost if the library has the package name embedded in it. - if (library.package.namePieces - .intersection(library.namePieces) - .isNotEmpty) { + if (libraryNamePieces.contains(library.package.name)) { scoredCandidate._alterScore(1.0, _Reason.packageName); } // Give a tiny boost for libraries with long names, assuming they're // more specific (and therefore more likely to be the owner of this symbol). scoredCandidate._alterScore( - .01 * library.namePieces.length, _Reason.longName); + .01 * libraryNamePieces.length, _Reason.longName); // If we don't know the location of this element (which shouldn't be // possible), return our best guess. @@ -81,7 +86,7 @@ abstract mixin class Canonicalization // The more pieces we have of the location in our library name, the more we // should boost our score. scoredCandidate._alterScore( - library.namePieces.intersection(elementLocationPieces).length.toDouble() / + libraryNamePieces.intersection(elementLocationPieces).length.toDouble() / elementLocationPieces.length.toDouble(), _Reason.sharedNamePart, ); @@ -90,7 +95,7 @@ abstract mixin class Canonicalization // boost the score a little bit. var scoreBoost = 0.0; for (var piece in elementLocationPieces.expand((item) => item.split('_'))) { - for (var namePiece in library.namePieces) { + for (var namePiece in libraryNamePieces) { if (piece.startsWith(namePiece)) { scoreBoost += 0.001; } @@ -101,6 +106,9 @@ abstract mixin class Canonicalization } } +/// A pattern that can split [Locatable.location] strings. +final _locationSplitter = RegExp(r'(package:|[\\/;.])'); + /// This class represents the score for a particular element; how likely /// it is that this is the canonical element. class _ScoredCandidate implements Comparable<_ScoredCandidate> { diff --git a/lib/src/model/category.dart b/lib/src/model/category.dart index e79b7d84b7..e6383df1db 100644 --- a/lib/src/model/category.dart +++ b/lib/src/model/category.dart @@ -16,7 +16,6 @@ class Category Warnable, CommentReferable, Locatable, - Canonicalization, MarkdownFileDocumentation, LibraryContainer, TopLevelContainer, diff --git a/lib/src/model/documentable.dart b/lib/src/model/documentable.dart index dad3039354..812bcb616f 100644 --- a/lib/src/model/documentable.dart +++ b/lib/src/model/documentable.dart @@ -5,6 +5,7 @@ import 'package:analyzer/file_system/file_system.dart'; import 'package:dartdoc/src/dartdoc_options.dart'; import 'package:dartdoc/src/io_utils.dart'; +import 'package:dartdoc/src/warnings.dart'; import 'model.dart'; @@ -53,7 +54,7 @@ enum DocumentLocation { remote, } -mixin MarkdownFileDocumentation implements Documentable, Canonicalization { +mixin MarkdownFileDocumentation implements Documentable, Warnable { DocumentLocation get documentedWhere; late final Documentation _documentation = Documentation.forElement(this); @@ -83,7 +84,4 @@ mixin MarkdownFileDocumentation implements Documentable, Canonicalization { @override String get location => '(${documentationFile?.path})'; - - @override - Set get locationPieces => {location}; } diff --git a/lib/src/model/documentation.dart b/lib/src/model/documentation.dart index c5857b31ea..c11045f8e4 100644 --- a/lib/src/model/documentation.dart +++ b/lib/src/model/documentation.dart @@ -3,12 +3,12 @@ // BSD-style license that can be found in the LICENSE file. import 'package:dartdoc/src/markdown_processor.dart'; -import 'package:dartdoc/src/model/model.dart'; import 'package:dartdoc/src/render/documentation_renderer.dart'; +import 'package:dartdoc/src/warnings.dart'; import 'package:markdown/markdown.dart' as md; class Documentation { - final Canonicalization _element; + final Warnable _element; Documentation.forElement(this._element); diff --git a/lib/src/model/documentation_comment.dart b/lib/src/model/documentation_comment.dart index 534f83bf72..1365373f26 100644 --- a/lib/src/model/documentation_comment.dart +++ b/lib/src/model/documentation_comment.dart @@ -5,7 +5,6 @@ import 'package:analyzer/dart/element/element.dart'; import 'package:args/args.dart'; import 'package:crypto/crypto.dart' as crypto; -import 'package:dartdoc/src/model/canonicalization.dart'; import 'package:dartdoc/src/model/documentable.dart'; import 'package:dartdoc/src/model/documentation.dart'; import 'package:dartdoc/src/model/inheritable.dart'; @@ -36,7 +35,7 @@ final _htmlInjectRegExp = RegExp(r'([a-f0-9]+)'); /// [_processCommentWithoutTools] and [processComment] are the primary /// entrypoints. mixin DocumentationComment - implements Canonicalization, Documentable, Warnable, Locatable, SourceCode { + implements Documentable, Warnable, Locatable, SourceCode { @override Element get element; diff --git a/lib/src/model/locatable.dart b/lib/src/model/locatable.dart index b3d0afb27e..6198d74b02 100644 --- a/lib/src/model/locatable.dart +++ b/lib/src/model/locatable.dart @@ -27,9 +27,16 @@ mixin Locatable { /// A string indicating the URI of this Locatable, usually derived from /// [Element.location]. String get location; -} -final RegExp locationSplitter = RegExp(r'(package:|[\\/;.])'); + /// Whether this is the "canonical" copy of an element. + /// + /// Generally, a canonical element must be public, along with possible other + /// requirements. + /// + /// In order for an element to be documented, it must be canonical, and have + /// documentation. + bool get isCanonical; +} extension NullableLocatable on Locatable? { String get safeWarnableName => diff --git a/lib/src/model/model_element.dart b/lib/src/model/model_element.dart index 52e9da0443..85322f63c3 100644 --- a/lib/src/model/model_element.dart +++ b/lib/src/model/model_element.dart @@ -59,7 +59,7 @@ import 'package:path/path.dart' as p show Context; /// helps prevent subtle bugs as generated output for a non-canonical /// ModelElement will reference itself as part of the "wrong" [Library] from the /// public interface perspective. -abstract class ModelElement extends Canonicalization +abstract class ModelElement with CommentReferable, Warnable, @@ -397,13 +397,6 @@ abstract class ModelElement extends Canonicalization DartdocOptionContext.fromContextElement( packageGraph.config, library.element, packageGraph.resourceProvider); - @override - late final Set locationPieces = element.location - .toString() - .split(locationSplitter) - .where((s) => s.isNotEmpty) - .toSet(); - bool get hasAttributes => attributes.isNotEmpty; /// This element's attributes. @@ -536,7 +529,8 @@ abstract class ModelElement extends Canonicalization var topLevelModelElement = ModelElement.forElement(topLevelElement, packageGraph); - return topLevelModelElement.calculateCanonicalCandidate(candidateLibraries); + return Canonicalization(topLevelModelElement) + .calculateCanonicalCandidate(candidateLibraries); } @override diff --git a/lib/src/model/nameable.dart b/lib/src/model/nameable.dart index 82bedfda7e..24b7c4c1da 100644 --- a/lib/src/model/nameable.dart +++ b/lib/src/model/nameable.dart @@ -13,29 +13,20 @@ import 'package:dartdoc/src/model/model_element.dart'; import 'package:dartdoc/src/model/package_graph.dart'; import 'package:dartdoc/src/model/privacy.dart'; -import 'locatable.dart'; - /// Something that has a name. mixin Nameable implements Privacy { String get name; + /// A qualified name, mostly for use in the web search functionality, and for + /// warnings printed in the terminal; not for display use in rendered HTML. String get fullyQualifiedName => name; - late final Set namePieces = { - ...name.split(locationSplitter).where((s) => s.isNotEmpty) - }; - /// The name to use as text in the rendered documentation. String get displayName => name; /// The name to use in breadcrumbs in the rendered documentation. String get breadcrumbName => name; - /// Utility getter/cache for `_MarkdownCommentReference._getResultsForClass`. - // TODO(jcollins-g): This should really be the same as 'name', but isn't - // because of accessors and operators. - late final String namePart = fullyQualifiedName.split('.').last; - @override bool get isPublic => name.isNotEmpty && !name.startsWith('_'); diff --git a/lib/src/model/package.dart b/lib/src/model/package.dart index 3e8c5914b3..de0fe839bd 100644 --- a/lib/src/model/package.dart +++ b/lib/src/model/package.dart @@ -29,7 +29,7 @@ const String htmlBasePlaceholder = r'%%__HTMLBASE_dartdoc_internal__%%'; /// A [LibraryContainer] that contains [Library] objects related to a particular /// package. class Package extends LibraryContainer - with Nameable, Locatable, Canonicalization, Warnable, CommentReferable + with Nameable, Locatable, Warnable, CommentReferable implements Privacy { @override final String name; @@ -82,9 +82,6 @@ class Package extends LibraryContainer // object that contains them. Map> usedAnimationIdsByHref = {}; - @override - Set get locationPieces => const {}; - /// Holds all libraries added to this package. May include non-documented /// libraries, but is not guaranteed to include a complete list of /// non-documented libraries unless they are all referenced by documented ones. diff --git a/lib/src/validator.dart b/lib/src/validator.dart index 46b74226eb..b6596f6ea0 100644 --- a/lib/src/validator.dart +++ b/lib/src/validator.dart @@ -9,7 +9,7 @@ import 'package:analyzer/file_system/file_system.dart'; import 'package:collection/collection.dart'; import 'package:dartdoc/src/dartdoc_options.dart'; import 'package:dartdoc/src/logging.dart'; -import 'package:dartdoc/src/model/canonicalization.dart'; +import 'package:dartdoc/src/model/locatable.dart'; import 'package:dartdoc/src/model/model_element.dart'; import 'package:dartdoc/src/model/package_graph.dart'; import 'package:dartdoc/src/runtime_stats.dart'; @@ -214,7 +214,7 @@ class Validator { String origin, { String? referredFrom, }) { - final referredFromElements = {}; + final referredFromElements = {}; // Make all paths relative to origin. if (path.isWithin(origin, warnOn)) {