Skip to content

Commit d9e31ff

Browse files
authored
Extract Canonicalization from the ModelElement hierarchy (#3753)
1 parent 1ae5b4c commit d9e31ff

13 files changed

+57
-201
lines changed

lib/src/generator/templates.aot_renderers_for_html.dart

+2-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import 'dart:convert';
1616

1717
import 'package:dartdoc/src/generator/template_data.dart';
1818
import 'package:dartdoc/src/model/accessor.dart';
19-
import 'package:dartdoc/src/model/canonicalization.dart';
2019
import 'package:dartdoc/src/model/category.dart';
2120
import 'package:dartdoc/src/model/class.dart';
2221
import 'package:dartdoc/src/model/constructor.dart';
@@ -37,6 +36,7 @@ import 'package:dartdoc/src/model/operator.dart';
3736
import 'package:dartdoc/src/model/package.dart';
3837
import 'package:dartdoc/src/model/top_level_variable.dart';
3938
import 'package:dartdoc/src/model/typedef.dart';
39+
import 'package:dartdoc/src/warnings.dart';
4040

4141
String renderCategory(CategoryTemplateData context0) {
4242
final buffer = StringBuffer();
@@ -3524,8 +3524,7 @@ String _deduplicated_lib_templates__head_html(TemplateDataBase context0) {
35243524
return buffer.toString();
35253525
}
35263526

3527-
String _deduplicated_lib_templates__documentation_html(
3528-
Canonicalization context0) {
3527+
String _deduplicated_lib_templates__documentation_html(Warnable context0) {
35293528
final buffer = StringBuffer();
35303529
if (context0.hasDocumentation) {
35313530
buffer.writeln();

lib/src/generator/templates.runtime_renderers.dart

+9-138
Original file line numberDiff line numberDiff line change
@@ -310,28 +310,6 @@ class _Renderer_Accessor extends RendererBase<Accessor> {
310310
parent: r);
311311
},
312312
),
313-
'namePart': Property(
314-
getValue: (CT_ c) => c.namePart,
315-
renderVariable:
316-
(CT_ c, Property<CT_> self, List<String> remainingNames) {
317-
if (remainingNames.isEmpty) {
318-
return self.getValue(c).toString();
319-
}
320-
var name = remainingNames.first;
321-
var nextProperty =
322-
_Renderer_String.propertyMap().getValue(name);
323-
return nextProperty.renderVariable(
324-
self.getValue(c) as String,
325-
nextProperty,
326-
[...remainingNames.skip(1)]);
327-
},
328-
isNullValue: (CT_ c) => false,
329-
renderValue: (CT_ c, RendererBase<CT_> r,
330-
List<MustachioNode> ast, StringSink sink) {
331-
_render_String(c.namePart, ast, r.template, sink,
332-
parent: r);
333-
},
334-
),
335313
'originalMember': Property(
336314
getValue: (CT_ c) => c.originalMember,
337315
renderVariable: (CT_ c, Property<CT_> self,
@@ -761,49 +739,6 @@ class _Renderer_CanonicalFor extends RendererBase<CanonicalFor> {
761739
}
762740
}
763741

764-
class _Renderer_Canonicalization extends RendererBase<Canonicalization> {
765-
static final Map<Type, Object> _propertyMapCache = {};
766-
static Map<String, Property<CT_>> propertyMap<
767-
CT_ extends Canonicalization>() =>
768-
_propertyMapCache.putIfAbsent(
769-
CT_,
770-
() => {
771-
..._Renderer_Object.propertyMap<CT_>(),
772-
'isCanonical': Property(
773-
getValue: (CT_ c) => c.isCanonical,
774-
renderVariable: (CT_ c, Property<CT_> self,
775-
List<String> remainingNames) =>
776-
self.renderSimpleVariable(c, remainingNames, 'bool'),
777-
getBool: (CT_ c) => c.isCanonical,
778-
),
779-
'locationPieces': Property(
780-
getValue: (CT_ c) => c.locationPieces,
781-
renderVariable: (CT_ c, Property<CT_> self,
782-
List<String> remainingNames) =>
783-
self.renderSimpleVariable(
784-
c, remainingNames, 'Set<String>'),
785-
renderIterable: (CT_ c, RendererBase<CT_> r,
786-
List<MustachioNode> ast, StringSink sink) {
787-
return c.locationPieces.map((e) =>
788-
_render_String(e, ast, r.template, sink, parent: r));
789-
},
790-
),
791-
}) as Map<String, Property<CT_>>;
792-
793-
_Renderer_Canonicalization(Canonicalization context,
794-
RendererBase<Object>? parent, Template template, StringSink sink)
795-
: super(context, parent, template, sink);
796-
797-
@override
798-
Property<Canonicalization>? getProperty(String key) {
799-
if (propertyMap<Canonicalization>().containsKey(key)) {
800-
return propertyMap<Canonicalization>()[key];
801-
} else {
802-
return null;
803-
}
804-
}
805-
}
806-
807742
class _Renderer_Categorization extends RendererBase<Categorization> {
808743
static final Map<Type, Object> _propertyMapCache = {};
809744
static Map<String, Property<CT_>> propertyMap<CT_ extends Categorization>() =>
@@ -903,7 +838,6 @@ class _Renderer_Category extends RendererBase<Category> {
903838
..._Renderer_Warnable.propertyMap<CT_>(),
904839
..._Renderer_CommentReferable.propertyMap<CT_>(),
905840
..._Renderer_Locatable.propertyMap<CT_>(),
906-
..._Renderer_Canonicalization.propertyMap<CT_>(),
907841
..._Renderer_MarkdownFileDocumentation.propertyMap<CT_>(),
908842
..._Renderer_LibraryContainer.propertyMap<CT_>(),
909843
..._Renderer_TopLevelContainer.propertyMap<CT_>(),
@@ -8984,6 +8918,13 @@ class _Renderer_Locatable extends RendererBase<Locatable> {
89848918
_render_String(c.href!, ast, r.template, sink, parent: r);
89858919
},
89868920
),
8921+
'isCanonical': Property(
8922+
getValue: (CT_ c) => c.isCanonical,
8923+
renderVariable: (CT_ c, Property<CT_> self,
8924+
List<String> remainingNames) =>
8925+
self.renderSimpleVariable(c, remainingNames, 'bool'),
8926+
getBool: (CT_ c) => c.isCanonical,
8927+
),
89878928
'location': Property(
89888929
getValue: (CT_ c) => c.location,
89898930
renderVariable:
@@ -9136,18 +9077,6 @@ class _Renderer_MarkdownFileDocumentation
91369077
parent: r);
91379078
},
91389079
),
9139-
'locationPieces': Property(
9140-
getValue: (CT_ c) => c.locationPieces,
9141-
renderVariable: (CT_ c, Property<CT_> self,
9142-
List<String> remainingNames) =>
9143-
self.renderSimpleVariable(
9144-
c, remainingNames, 'Set<String>'),
9145-
renderIterable: (CT_ c, RendererBase<CT_> r,
9146-
List<MustachioNode> ast, StringSink sink) {
9147-
return c.locationPieces.map((e) =>
9148-
_render_String(e, ast, r.template, sink, parent: r));
9149-
},
9150-
),
91519080
'oneLineDoc': Property(
91529081
getValue: (CT_ c) => c.oneLineDoc,
91539082
renderVariable:
@@ -10102,7 +10031,7 @@ class _Renderer_ModelElement extends RendererBase<ModelElement> {
1010210031
_propertyMapCache.putIfAbsent(
1010310032
CT_,
1010410033
() => {
10105-
..._Renderer_Canonicalization.propertyMap<CT_>(),
10034+
..._Renderer_Object.propertyMap<CT_>(),
1010610035
..._Renderer_CommentReferable.propertyMap<CT_>(),
1010710036
..._Renderer_Warnable.propertyMap<CT_>(),
1010810037
..._Renderer_Locatable.propertyMap<CT_>(),
@@ -10748,18 +10677,6 @@ class _Renderer_ModelElement extends RendererBase<ModelElement> {
1074810677
parent: r);
1074910678
},
1075010679
),
10751-
'locationPieces': Property(
10752-
getValue: (CT_ c) => c.locationPieces,
10753-
renderVariable: (CT_ c, Property<CT_> self,
10754-
List<String> remainingNames) =>
10755-
self.renderSimpleVariable(
10756-
c, remainingNames, 'Set<String>'),
10757-
renderIterable: (CT_ c, RendererBase<CT_> r,
10758-
List<MustachioNode> ast, StringSink sink) {
10759-
return c.locationPieces.map((e) =>
10760-
_render_String(e, ast, r.template, sink, parent: r));
10761-
},
10762-
),
1076310680
'modelNode': Property(
1076410681
getValue: (CT_ c) => c.modelNode,
1076510682
renderVariable: (CT_ c, Property<CT_> self,
@@ -11384,40 +11301,6 @@ class _Renderer_Nameable extends RendererBase<Nameable> {
1138411301
_render_String(c.name, ast, r.template, sink, parent: r);
1138511302
},
1138611303
),
11387-
'namePart': Property(
11388-
getValue: (CT_ c) => c.namePart,
11389-
renderVariable:
11390-
(CT_ c, Property<CT_> self, List<String> remainingNames) {
11391-
if (remainingNames.isEmpty) {
11392-
return self.getValue(c).toString();
11393-
}
11394-
var name = remainingNames.first;
11395-
var nextProperty =
11396-
_Renderer_String.propertyMap().getValue(name);
11397-
return nextProperty.renderVariable(
11398-
self.getValue(c) as String,
11399-
nextProperty,
11400-
[...remainingNames.skip(1)]);
11401-
},
11402-
isNullValue: (CT_ c) => false,
11403-
renderValue: (CT_ c, RendererBase<CT_> r,
11404-
List<MustachioNode> ast, StringSink sink) {
11405-
_render_String(c.namePart, ast, r.template, sink,
11406-
parent: r);
11407-
},
11408-
),
11409-
'namePieces': Property(
11410-
getValue: (CT_ c) => c.namePieces,
11411-
renderVariable: (CT_ c, Property<CT_> self,
11412-
List<String> remainingNames) =>
11413-
self.renderSimpleVariable(
11414-
c, remainingNames, 'Set<String>'),
11415-
renderIterable: (CT_ c, RendererBase<CT_> r,
11416-
List<MustachioNode> ast, StringSink sink) {
11417-
return c.namePieces.map((e) =>
11418-
_render_String(e, ast, r.template, sink, parent: r));
11419-
},
11420-
),
1142111304
'packageGraph': Property(
1142211305
getValue: (CT_ c) => c.packageGraph,
1142311306
renderVariable: (CT_ c, Property<CT_> self,
@@ -11667,7 +11550,6 @@ class _Renderer_Package extends RendererBase<Package> {
1166711550
..._Renderer_LibraryContainer.propertyMap<CT_>(),
1166811551
..._Renderer_Nameable.propertyMap<CT_>(),
1166911552
..._Renderer_Locatable.propertyMap<CT_>(),
11670-
..._Renderer_Canonicalization.propertyMap<CT_>(),
1167111553
..._Renderer_Warnable.propertyMap<CT_>(),
1167211554
..._Renderer_CommentReferable.propertyMap<CT_>(),
1167311555
'aboveSidebarPath': Property(
@@ -12128,18 +12010,6 @@ class _Renderer_Package extends RendererBase<Package> {
1212812010
parent: r);
1212912011
},
1213012012
),
12131-
'locationPieces': Property(
12132-
getValue: (CT_ c) => c.locationPieces,
12133-
renderVariable: (CT_ c, Property<CT_> self,
12134-
List<String> remainingNames) =>
12135-
self.renderSimpleVariable(
12136-
c, remainingNames, 'Set<String>'),
12137-
renderIterable: (CT_ c, RendererBase<CT_> r,
12138-
List<MustachioNode> ast, StringSink sink) {
12139-
return c.locationPieces.map((e) =>
12140-
_render_String(e, ast, r.template, sink, parent: r));
12141-
},
12142-
),
1214312013
'name': Property(
1214412014
getValue: (CT_ c) => c.name,
1214512015
renderVariable:
@@ -16353,6 +16223,7 @@ const _invisibleGetters = {
1635316223
'documentationIsLocal',
1635416224
'fullyQualifiedName',
1635516225
'href',
16226+
'isCanonical',
1635616227
'location'
1635716228
},
1635816229
'Map': {

lib/src/model/accessor.dart

+2-9
Original file line numberDiff line numberDiff line change
@@ -156,22 +156,15 @@ class Accessor extends ModelElement {
156156
@override
157157
Kind get kind => Kind.accessor;
158158

159-
String get _namePart => super.namePart.split('=').first;
160-
161-
@override
162-
String get namePart => _namePart;
163-
164-
@override
165-
166159
/// Accessors should never be participating directly in comment reference
167160
/// lookups.
161+
@override
168162
Map<String, CommentReferable> get referenceChildren =>
169163
enclosingCombo.referenceChildren;
170164

171-
@override
172-
173165
/// Accessors should never be participating directly in comment reference
174166
/// lookups.
167+
@override
175168
Iterable<CommentReferable> get referenceParents =>
176169
enclosingCombo.referenceParents;
177170
}

lib/src/model/canonicalization.dart

+22-14
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,20 @@ import 'package:dartdoc/src/warnings.dart';
1010
/// This provides heuristic scoring to determine which library a human likely
1111
/// considers this element to be primarily 'from', and therefore, canonical.
1212
/// Still warn if the heuristic isn't very confident.
13-
abstract mixin class Canonicalization
14-
implements Locatable, Documentable, Warnable {
15-
bool get isCanonical;
13+
final class Canonicalization {
14+
final ModelElement _element;
1615

17-
/// Pieces of the location, split to remove 'package:' and slashes.
18-
Set<String> get locationPieces;
16+
Canonicalization(this._element);
1917

2018
Library calculateCanonicalCandidate(Iterable<Library> libraries) {
19+
var locationPieces = _element.element.location
20+
.toString()
21+
.split(_locationSplitter)
22+
.where((s) => s.isNotEmpty)
23+
.toSet();
2124
var scoredCandidates = libraries
2225
.map((library) => _scoreElementWithLibrary(
23-
library, fullyQualifiedName, locationPieces))
26+
library, _element.fullyQualifiedName, locationPieces))
2427
.toList(growable: false)
2528
..sort();
2629

@@ -31,11 +34,11 @@ abstract mixin class Canonicalization
3134
var confidence = highestScore - secondHighestScore;
3235
final canonicalLibrary = librariesByScore.last;
3336

34-
if (confidence < config.ambiguousReexportScorerMinConfidence) {
37+
if (confidence < _element.config.ambiguousReexportScorerMinConfidence) {
3538
var libraryNames = librariesByScore.map((l) => l.name);
3639
var message = '$libraryNames -> ${canonicalLibrary.name} '
3740
'(confidence ${confidence.toStringAsPrecision(4)})';
38-
warn(PackageWarning.ambiguousReexport,
41+
_element.warn(PackageWarning.ambiguousReexport,
3942
message: message, extendedDebug: scoredCandidates.map((s) => '$s'));
4043
}
4144

@@ -61,17 +64,19 @@ abstract mixin class Canonicalization
6164
scoredCandidate._alterScore(-1.0, _Reason.deprecated);
6265
}
6366

67+
var libraryNamePieces = {
68+
...library.name.split('.').where((s) => s.isNotEmpty)
69+
};
70+
6471
// Give a big boost if the library has the package name embedded in it.
65-
if (library.package.namePieces
66-
.intersection(library.namePieces)
67-
.isNotEmpty) {
72+
if (libraryNamePieces.contains(library.package.name)) {
6873
scoredCandidate._alterScore(1.0, _Reason.packageName);
6974
}
7075

7176
// Give a tiny boost for libraries with long names, assuming they're
7277
// more specific (and therefore more likely to be the owner of this symbol).
7378
scoredCandidate._alterScore(
74-
.01 * library.namePieces.length, _Reason.longName);
79+
.01 * libraryNamePieces.length, _Reason.longName);
7580

7681
// If we don't know the location of this element (which shouldn't be
7782
// possible), return our best guess.
@@ -81,7 +86,7 @@ abstract mixin class Canonicalization
8186
// The more pieces we have of the location in our library name, the more we
8287
// should boost our score.
8388
scoredCandidate._alterScore(
84-
library.namePieces.intersection(elementLocationPieces).length.toDouble() /
89+
libraryNamePieces.intersection(elementLocationPieces).length.toDouble() /
8590
elementLocationPieces.length.toDouble(),
8691
_Reason.sharedNamePart,
8792
);
@@ -90,7 +95,7 @@ abstract mixin class Canonicalization
9095
// boost the score a little bit.
9196
var scoreBoost = 0.0;
9297
for (var piece in elementLocationPieces.expand((item) => item.split('_'))) {
93-
for (var namePiece in library.namePieces) {
98+
for (var namePiece in libraryNamePieces) {
9499
if (piece.startsWith(namePiece)) {
95100
scoreBoost += 0.001;
96101
}
@@ -101,6 +106,9 @@ abstract mixin class Canonicalization
101106
}
102107
}
103108

109+
/// A pattern that can split [Locatable.location] strings.
110+
final _locationSplitter = RegExp(r'(package:|[\\/;.])');
111+
104112
/// This class represents the score for a particular element; how likely
105113
/// it is that this is the canonical element.
106114
class _ScoredCandidate implements Comparable<_ScoredCandidate> {

lib/src/model/category.dart

-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ class Category
1616
Warnable,
1717
CommentReferable,
1818
Locatable,
19-
Canonicalization,
2019
MarkdownFileDocumentation,
2120
LibraryContainer,
2221
TopLevelContainer,

0 commit comments

Comments
 (0)