Skip to content

Commit

Permalink
Frame for merging scoped lookups (#2661)
Browse files Browse the repository at this point in the history
* Frame for merging scoped lookups

* rebuild

* Review comments
  • Loading branch information
jcollins-g committed May 27, 2021
1 parent 8ec6844 commit 0922660
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 15 deletions.
1 change: 1 addition & 0 deletions lib/src/element_type.dart
Expand Up @@ -16,6 +16,7 @@ import 'package:dartdoc/src/render/element_type_renderer.dart';
/// may link to a [ModelElement].
abstract class ElementType extends Privacy with CommentReferable, Nameable {
final DartType _type;
@override
final PackageGraph packageGraph;
final ElementType returnedFrom;
@override
Expand Down
24 changes: 24 additions & 0 deletions lib/src/generator/templates.runtime_renderers.dart
Expand Up @@ -2586,6 +2586,19 @@ class _Renderer_CommentReferable extends RendererBase<CommentReferable> {
parent: r);
},
),
'packageGraph': Property(
getValue: (CT_ c) => c.packageGraph,
renderVariable: (CT_ c, Property<CT_> self,
List<String> remainingNames) =>
self.renderSimpleVariable(
c, remainingNames, 'PackageGraph'),
isNullValue: (CT_ c) => c.packageGraph == null,
renderValue:
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
return renderSimple(c.packageGraph, ast, r.template,
parent: r);
},
),
'referenceChildren': Property(
getValue: (CT_ c) => c.referenceChildren,
renderVariable: (CT_ c, Property<CT_> self,
Expand All @@ -2611,6 +2624,17 @@ class _Renderer_CommentReferable extends RendererBase<CommentReferable> {
(e) => renderSimple(e, ast, r.template, parent: r));
},
),
'scope': Property(
getValue: (CT_ c) => c.scope,
renderVariable: (CT_ c, Property<CT_> self,
List<String> remainingNames) =>
self.renderSimpleVariable(c, remainingNames, 'Scope'),
isNullValue: (CT_ c) => c.scope == null,
renderValue:
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
return renderSimple(c.scope, ast, r.template, parent: r);
},
),
});

_Renderer_CommentReferable(
Expand Down
89 changes: 75 additions & 14 deletions lib/src/model/comment_referable.dart
Expand Up @@ -10,17 +10,37 @@ library dartdoc.src.model.comment_reference;
import 'dart:core';

import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/scope.dart';
import 'package:dartdoc/dartdoc.dart';
import 'package:meta/meta.dart';

class ReferenceChildrenLookup {
final String lookup;
final List<String> remaining;

ReferenceChildrenLookup(this.lookup, this.remaining);

@override
String toString() => '$lookup.${remaining.join(".")}';
}

extension on Scope {
/// Prefer the getter for a bundled lookup if both exist.
Element lookupPreferGetter(String id) {
var result = lookup(id);
return result.getter ?? result.setter;
}
}

/// Support comment reference lookups on a Nameable object.
mixin CommentReferable implements Nameable {
PackageGraph packageGraph;

/// For any [CommentReferable] where an analyzer [Scope] exists (or can
/// be constructed), implement this. This will take priority over
/// lookups via [referenceChildren]. Can be cached.
Scope get scope => null;

/// Look up a comment reference by its component parts. If [tryParents] is
/// true, try looking up the same reference in any parents of [this].
/// Will skip over results that do not pass a given [filter] and keep
Expand All @@ -37,20 +57,14 @@ mixin CommentReferable implements Nameable {

/// Search for the reference.
for (var referenceLookup in childLookups(reference)) {
if (scope != null) {
result = lookupViaScope(referenceLookup, filter);
if (result != null) break;
}
if (referenceChildren.containsKey(referenceLookup.lookup)) {
result = referenceChildren[referenceLookup.lookup];
if (referenceLookup.remaining.isNotEmpty) {
result = result?.referenceBy(referenceLookup.remaining,
tryParents: false, filter: filter);
} else if (!filter(result)) {
result = result?.referenceBy([referenceLookup.lookup],
tryParents: false, filter: filter);
}
if (!filter(result)) {
result = null;
}
result = _lookupViaReferenceChildren(referenceLookup, filter);
if (result != null) break;
}
if (result != null) break;
}
// If we can't find it in children, try searching parents if allowed.
if (result == null && tryParents) {
Expand All @@ -62,6 +76,51 @@ mixin CommentReferable implements Nameable {
return result;
}

/// Looks up references by [scope], skipping over results that do not match
/// the given filter.
///
/// Override if [Scope.lookup] may return a [PrefixElement] or other elements
/// not corresponding to a [CommentReferable], but you still want to have
/// an implementation of [scope].
CommentReferable lookupViaScope(ReferenceChildrenLookup referenceLookup,
bool Function(CommentReferable) filter) {
var resultElement = scope.lookupPreferGetter(referenceLookup.lookup);
if (resultElement is PrefixElement) {
assert(false,
'PrefixElement detected, override [lookupViaScope] in subclass');
return null;
}
return recurseChildrenAndFilter(referenceLookup,
ModelElement.fromElement(resultElement, packageGraph), filter);
}

CommentReferable _lookupViaReferenceChildren(
ReferenceChildrenLookup referenceLookup,
bool Function(CommentReferable) filter) =>
recurseChildrenAndFilter(
referenceLookup, referenceChildren[referenceLookup.lookup], filter);

/// Given a [result] found in an implementation of [lookupViaScope] or
/// [_lookupViaReferenceChildren], recurse through children, skipping over
/// results that do not match the filter.
CommentReferable recurseChildrenAndFilter(
ReferenceChildrenLookup referenceLookup,
CommentReferable result,
bool Function(CommentReferable) filter) {
assert(result != null);
if (referenceLookup.remaining.isNotEmpty) {
result = result.referenceBy(referenceLookup.remaining,
tryParents: false, filter: filter);
} else if (!filter(result)) {
result = result.referenceBy([referenceLookup.lookup],
tryParents: false, filter: filter);
}
if (!filter(result)) {
result = null;
}
return result;
}

/// A list of lookups that should be attempted on children based on
/// [reference]. This allows us to deal with libraries that may have
/// separators in them. [referenceBy] stops at the first one found.
Expand All @@ -71,8 +130,10 @@ mixin CommentReferable implements Nameable {
];

/// Map of name to the elements that are a member of [this], but
/// not this model element itself.
/// Can be cached.
/// not this model element itself. Can be cached.
///
/// There is no need to duplicate references here that can be found via
/// [scope].
Map<String, CommentReferable> get referenceChildren;

/// Iterable of immediate "parents" to try resolving component parts.
Expand Down
1 change: 1 addition & 0 deletions lib/src/model/package_graph.dart
Expand Up @@ -267,6 +267,7 @@ class PackageGraph with CommentReferable, Nameable {

bool get hasFooterVersion => !config.excludeFooterVersion;

@override
PackageGraph get packageGraph => this;

/// Map of package name to Package.
Expand Down
2 changes: 1 addition & 1 deletion test/comment_referable/comment_referable_test.dart
Expand Up @@ -25,7 +25,7 @@ abstract class Base extends Nameable with CommentReferable {
Element get element => throw UnimplementedError();
}

class Top extends Base with CommentReferable {
class Top extends Base {
@override
final String name;
final List<TopChild> children;
Expand Down

0 comments on commit 0922660

Please sign in to comment.