Skip to content

Commit 342d2d7

Browse files
authored
Major refactor of dartdoc "features" and annotations (#2600)
* beginning * first test * test * begin extracting annotation * dartfmt, comment * Update skip * Rebuild renderers * Beginning feature refactor * Getting there * obliterate strings * Final cleanups * Bypass @Native crashing us in flutter * Restrict ast import * Expand feature rendering out and get span classes fixed in parameters * dartfmt * review comments
1 parent 2b129d5 commit 342d2d7

26 files changed

+796
-384
lines changed

lib/src/generator/templates.renderers.dart

Lines changed: 133 additions & 138 deletions
Large diffs are not rendered by default.

lib/src/model/annotation.dart

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:analyzer/dart/element/element.dart';
6+
import 'package:dartdoc/src/element_type.dart';
7+
import 'package:dartdoc/src/model/feature.dart';
8+
import 'package:dartdoc/src/model/getter_setter_combo.dart';
9+
import 'package:dartdoc/src/model/library.dart';
10+
import 'package:dartdoc/src/model/model_element.dart';
11+
import 'package:dartdoc/src/model/package_graph.dart';
12+
13+
/// Represents a Dart annotation, attached to an element in the source code with
14+
/// `@`.
15+
class Annotation extends Feature {
16+
final ElementAnnotation annotation;
17+
final Library library;
18+
final PackageGraph packageGraph;
19+
20+
Annotation(this.annotation, this.library, this.packageGraph)
21+
: super(annotation.element.name);
22+
23+
String _linkedNameWithParameters;
24+
@override
25+
String get linkedNameWithParameters => _linkedNameWithParameters ??=
26+
packageGraph.rendererFactory.featureRenderer.renderAnnotation(this);
27+
28+
/// Return the linked name of the annotation.
29+
@override
30+
String get linkedName => annotation.element is PropertyAccessorElement
31+
? ModelElement.fromElement(annotation.element, packageGraph).linkedName
32+
// TODO(jcollins-g): consider linking to constructor instead of type?
33+
: modelType.linkedName;
34+
35+
ElementType _modelType;
36+
ElementType get modelType {
37+
if (_modelType == null) {
38+
var annotatedWith = annotation.element;
39+
if (annotatedWith is ConstructorElement) {
40+
_modelType =
41+
ElementType.from(annotatedWith.returnType, library, packageGraph);
42+
} else if (annotatedWith is PropertyAccessorElement) {
43+
_modelType =
44+
(ModelElement.fromElement(annotatedWith.variable, packageGraph)
45+
as GetterSetterCombo)
46+
.modelType;
47+
} else {
48+
assert(false,
49+
'non-callable element used as annotation?: ${annotation.element}');
50+
}
51+
}
52+
return _modelType;
53+
}
54+
55+
String _parameterText;
56+
String get parameterText {
57+
// TODO(srawlins): Attempt to revive constructor arguments in an annotation,
58+
// akin to source_gen's Reviver, in order to link to inner components. For
59+
// example, in `@Foo(const Bar(), baz: <Baz>[Baz.one, Baz.two])`, link to
60+
// `Foo`, `Bar`, `Baz`, `Baz.one`, and `Baz.two`.
61+
if (_parameterText == null) {
62+
var source = annotation.toSource();
63+
var startIndex = source.indexOf('(');
64+
_parameterText =
65+
source.substring(startIndex == -1 ? source.length : startIndex);
66+
}
67+
return _parameterText;
68+
}
69+
70+
@override
71+
bool get isPublic =>
72+
modelType.isPublic &&
73+
modelType is DefinedElementType &&
74+
!packageGraph.invisibleAnnotations
75+
.contains((modelType as DefinedElementType).element);
76+
77+
@override
78+
bool operator ==(Object other) {
79+
if (other is Annotation) {
80+
return other.annotation == annotation;
81+
}
82+
return false;
83+
}
84+
85+
@override
86+
int get hashCode => annotation.hashCode;
87+
}

lib/src/model/constructor.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ class Constructor extends ModelElement
7878
@override
7979
String get kind => 'constructor';
8080

81-
DefinedElementType _modelType;
82-
DefinedElementType get modelType =>
81+
CallableElementTypeMixin _modelType;
82+
CallableElementTypeMixin get modelType =>
8383
_modelType ??= ElementType.from(element.type, library, packageGraph);
8484

8585
String _name;

lib/src/model/container_member.dart

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'package:dartdoc/src/model/feature.dart';
56
import 'package:dartdoc/src/model/model.dart';
67

78
/// A [ModelElement] that is a [Container] member.
@@ -25,11 +26,10 @@ mixin ContainerMember on ModelElement implements EnclosedElement {
2526
}
2627

2728
@override
28-
Set<String> get features {
29-
var _features = super.features;
30-
if (isExtended) _features.add('extended');
31-
return _features;
32-
}
29+
Set<Feature> get features => {
30+
...super.features,
31+
if (isExtended) Feature.extended,
32+
};
3333

3434
bool _canonicalEnclosingContainerIsSet = false;
3535
Container _canonicalEnclosingContainer;

lib/src/model/feature.dart

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:collection/collection.dart';
6+
import 'package:dartdoc/src/model/privacy.dart';
7+
8+
int byFeatureOrdering(Feature a, Feature b) {
9+
if (a.sortGroup < b.sortGroup) return -1;
10+
if (a.sortGroup > b.sortGroup) return 1;
11+
return compareAsciiLowerCaseNatural(a.name, b.name);
12+
}
13+
14+
class ElementFeatureNotFoundError extends Error {
15+
final String message;
16+
17+
ElementFeatureNotFoundError([this.message]);
18+
19+
@override
20+
String toString() => 'ElementFeatureNotFoundError: $message';
21+
}
22+
23+
/// A "feature" includes both explicit annotations in code (e.g. `deprecated`)
24+
/// as well as others added by the documentation system (`read-write`);
25+
class Feature implements Privacy {
26+
final String _name;
27+
28+
/// Do not use this except in subclasses, prefer const members of this
29+
/// class instead.
30+
const Feature(this._name, [this.sortGroup = 0]);
31+
32+
final String featurePrefix = '';
33+
34+
String get name => _name;
35+
36+
String get linkedName => name;
37+
38+
String get linkedNameWithParameters => linkedName;
39+
40+
@override
41+
bool get isPublic => !name.startsWith('_');
42+
43+
/// Numerical sort group for this feature.
44+
/// Less than zero will sort before custom annotations.
45+
/// Above zero will sort after custom annotations.
46+
/// Zero will sort alphabetically among custom annotations.
47+
// TODO(jcollins-g): consider [Comparable]?
48+
final int sortGroup;
49+
50+
static const readOnly = Feature('read-only', 1);
51+
static const finalFeature = Feature('final', 2);
52+
static const writeOnly = Feature('write-only', 2);
53+
static const readWrite = Feature('read / write', 2);
54+
static const covariant = Feature('covariant', 2);
55+
static const extended = Feature('extended', 3);
56+
static const inherited = Feature('inherited', 3);
57+
static const inheritedGetter = Feature('inherited-getter', 3);
58+
static const inheritedSetter = Feature('inherited-setter', 3);
59+
static const lateFeature = Feature('late', 3);
60+
static const overrideFeature = Feature('override', 3);
61+
static const overrideGetter = Feature('override-getter', 3);
62+
static const overrideSetter = Feature('override-setter', 3);
63+
}

lib/src/model/feature_set.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ mixin FeatureSet {
1818
// legacy interfaces.
1919
if (isNullSafety) {
2020
yield LanguageFeature(
21-
'Null safety', packageGraph.rendererFactory.featureRenderer);
21+
'Null safety', packageGraph.rendererFactory.languageFeatureRenderer);
2222
}
2323
}
2424

lib/src/model/field.dart

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'package:analyzer/dart/element/element.dart';
66
import 'package:analyzer/src/dart/element/element.dart';
7+
import 'package:dartdoc/src/model/feature.dart';
78
import 'package:dartdoc/src/model/model.dart';
89
import 'package:dartdoc/src/render/source_code_renderer.dart';
910

@@ -102,47 +103,35 @@ class Field extends ModelElement
102103
@override
103104
String get kind => isConst ? 'constant' : 'property';
104105

105-
@override
106-
List<String> get annotations {
107-
var allAnnotations = [...super.annotations];
108-
109-
if (element is PropertyInducingElement) {
110-
var pie = element as PropertyInducingElement;
111-
allAnnotations.addAll(annotationsFromMetadata(pie.getter?.metadata));
112-
allAnnotations.addAll(annotationsFromMetadata(pie.setter?.metadata));
113-
}
114-
return allAnnotations;
115-
}
116-
117106
String get fullkind {
118107
if (field.isAbstract) return 'abstract $kind';
119108
return kind;
120109
}
121110

122111
@override
123-
Set<String> get features {
112+
Set<Feature> get features {
124113
var allFeatures = super.features..addAll(comboFeatures);
125114
// Combo features can indicate 'inherited' and 'override' if
126115
// either the getter or setter has one of those properties, but that's not
127116
// really specific enough for [Field]s that have public getter/setters.
128117
if (hasPublicGetter && hasPublicSetter) {
129118
if (getter.isInherited && setter.isInherited) {
130-
allFeatures.add('inherited');
119+
allFeatures.add(Feature.inherited);
131120
} else {
132-
allFeatures.remove('inherited');
133-
if (getter.isInherited) allFeatures.add('inherited-getter');
134-
if (setter.isInherited) allFeatures.add('inherited-setter');
121+
allFeatures.remove(Feature.inherited);
122+
if (getter.isInherited) allFeatures.add(Feature.inheritedGetter);
123+
if (setter.isInherited) allFeatures.add(Feature.inheritedSetter);
135124
}
136125
if (getter.isOverride && setter.isOverride) {
137-
allFeatures.add('override');
126+
allFeatures.add(Feature.overrideFeature);
138127
} else {
139-
allFeatures.remove('override');
140-
if (getter.isOverride) allFeatures.add('override-getter');
141-
if (setter.isOverride) allFeatures.add('override-setter');
128+
allFeatures.remove(Feature.overrideFeature);
129+
if (getter.isOverride) allFeatures.add(Feature.overrideGetter);
130+
if (setter.isOverride) allFeatures.add(Feature.overrideSetter);
142131
}
143132
} else {
144-
if (isInherited) allFeatures.add('inherited');
145-
if (isOverride) allFeatures.add('override');
133+
if (isInherited) allFeatures.add(Feature.inherited);
134+
if (isOverride) allFeatures.add(Feature.overrideFeature);
146135
}
147136
return allFeatures;
148137
}

lib/src/model/getter_setter_combo.dart

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,40 +4,46 @@
44

55
import 'dart:convert';
66

7-
import 'package:analyzer/dart/ast/ast.dart';
7+
import 'package:analyzer/dart/ast/ast.dart'
8+
show Expression, InstanceCreationExpression;
89
import 'package:analyzer/dart/element/element.dart';
910
import 'package:analyzer/source/line_info.dart';
1011
import 'package:analyzer/src/dart/element/element.dart';
1112
import 'package:dartdoc/src/element_type.dart';
13+
import 'package:dartdoc/src/model/annotation.dart';
14+
import 'package:dartdoc/src/model/feature.dart';
1215
import 'package:dartdoc/src/model/model.dart';
1316
import 'package:dartdoc/src/utils.dart';
1417
import 'package:dartdoc/src/warnings.dart';
18+
import 'package:meta/meta.dart';
1519

1620
/// Mixin for top-level variables and fields (aka properties)
1721
mixin GetterSetterCombo on ModelElement {
1822
Accessor get getter;
1923

2024
Accessor get setter;
2125

26+
@override
27+
Iterable<Annotation> get annotations => [
28+
...super.annotations,
29+
if (hasGetter) ...getter.annotations,
30+
if (hasSetter) ...setter.annotations,
31+
];
32+
2233
Iterable<Accessor> get allAccessors sync* {
2334
for (var a in [getter, setter]) {
2435
if (a != null) yield a;
2536
}
2637
}
2738

28-
Set<String> get comboFeatures {
29-
var allFeatures = <String>{};
30-
if (hasExplicitGetter && hasPublicGetter) {
31-
allFeatures.addAll(getter.features);
32-
}
33-
if (hasExplicitSetter && hasPublicSetter) {
34-
allFeatures.addAll(setter.features);
35-
}
36-
if (readOnly && !isFinal && !isConst) allFeatures.add('read-only');
37-
if (writeOnly) allFeatures.add('write-only');
38-
if (readWrite) allFeatures.add('read / write');
39-
return allFeatures;
40-
}
39+
@protected
40+
Set<Feature> get comboFeatures => {
41+
if (hasExplicitGetter && hasPublicGetter) ...getter.features,
42+
if (hasExplicitSetter && hasPublicSetter) ...setter.features,
43+
if (readOnly && !isFinal && !isConst) Feature.readOnly,
44+
if (writeOnly) Feature.writeOnly,
45+
if (readWrite) Feature.readWrite,
46+
};
4147

4248
@override
4349
ModelElement enclosingElement;

lib/src/model/inheritable.dart

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'package:dartdoc/src/model/feature.dart';
56
import 'package:dartdoc/src/model/model.dart';
67
import 'package:dartdoc/src/special_elements.dart';
78

@@ -28,13 +29,12 @@ mixin Inheritable on ContainerMember {
2829
bool get isCovariant;
2930

3031
@override
31-
Set<String> get features {
32-
var _features = super.features;
33-
if (isOverride) _features.add('override');
34-
if (isInherited) _features.add('inherited');
35-
if (isCovariant) _features.add('covariant');
36-
return _features;
37-
}
32+
Set<Feature> get features => {
33+
...super.features,
34+
if (isOverride) Feature.overrideFeature,
35+
if (isInherited) Feature.inherited,
36+
if (isCovariant) Feature.covariant,
37+
};
3838

3939
@override
4040
Library get canonicalLibrary => canonicalEnclosingContainer?.canonicalLibrary;

lib/src/model/language_feature.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
import 'package:dartdoc/src/render/feature_renderer.dart';
5+
import 'package:dartdoc/src/render/language_feature_renderer.dart';
66

77
const Map<String, String> _featureDescriptions = {
88
'Null safety': 'Supports the null safety language feature.',
@@ -23,12 +23,12 @@ class LanguageFeature {
2323
String /*?*/ get featureUrl => _featureUrls[name];
2424

2525
/// The rendered label for this language feature.
26-
String get featureLabel => _featureRenderer.renderFeatureLabel(this);
26+
String get featureLabel => _featureRenderer.renderLanguageFeatureLabel(this);
2727

2828
/// The name of this language feature.
2929
final String name;
3030

31-
final FeatureRenderer _featureRenderer;
31+
final LanguageFeatureRenderer _featureRenderer;
3232

3333
LanguageFeature(this.name, this._featureRenderer) {
3434
assert(_featureDescriptions.containsKey(name));

lib/src/model/method.dart

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:analyzer/dart/element/element.dart';
66
import 'package:analyzer/source/line_info.dart';
77
import 'package:analyzer/src/dart/element/member.dart' show ExecutableMember;
88
import 'package:dartdoc/src/element_type.dart';
9+
import 'package:dartdoc/src/model/feature.dart';
910
import 'package:dartdoc/src/model/model.dart';
1011

1112
class Method extends ModelElement
@@ -81,11 +82,10 @@ class Method extends ModelElement
8182
bool get isOperator => false;
8283

8384
@override
84-
Set<String> get features {
85-
var allFeatures = super.features;
86-
if (isInherited) allFeatures.add('inherited');
87-
return allFeatures;
88-
}
85+
Set<Feature> get features => {
86+
...super.features,
87+
if (isInherited) Feature.inherited,
88+
};
8989

9090
@override
9191
bool get isStatic => element.isStatic;

0 commit comments

Comments
 (0)