Skip to content

Commit c8134e5

Browse files
authored
Look up code references within libraries by map rather than traversing a list (#1829)
* Extremely basic extraction of helper methods from _findRefElementInLibrary and optimize lookup for _findPartiallyQualifiedMatches * Cleanup and some comments * Clarify the 'removing null' dance and add a TODO
1 parent 6580712 commit c8134e5

File tree

2 files changed

+134
-94
lines changed

2 files changed

+134
-94
lines changed

lib/src/markdown_processor.dart

Lines changed: 120 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,9 @@ ModelElement _findRefElementInLibrary(String codeRef, Warnable element,
333333
newCodeRef, element, commentRefs, preferredClass);
334334
}
335335

336+
// Remove any "null" objects after each step of trying to add to results.
337+
// TODO(jcollins-g): Eliminate all situations where nulls can be added
338+
// to the results set.
336339
results.remove(null);
337340
// Oh, and someone might have some type parameters or other garbage.
338341
if (results.isEmpty && codeRef.contains(trailingIgnoreStuff)) {
@@ -366,118 +369,30 @@ ModelElement _findRefElementInLibrary(String codeRef, Warnable element,
366369
p.name == codeRefChomped || codeRefChomped.startsWith("${p.name}.")));
367370
}
368371
}
369-
370372
results.remove(null);
371-
if (results.isEmpty) {
372-
// Maybe this is local to a class.
373-
// TODO(jcollins-g): tryClasses is a strict subset of the superclass chain. Optimize.
374-
List<Class> tryClasses = [preferredClass];
375-
Class realClass = tryClasses.first;
376-
if (element is Inheritable) {
377-
Inheritable overriddenElement = element.overriddenElement;
378-
while (overriddenElement != null) {
379-
tryClasses.add(
380-
(element.overriddenElement as EnclosedElement).enclosingElement);
381-
overriddenElement = overriddenElement.overriddenElement;
382-
}
383-
}
384-
385-
for (Class tryClass in tryClasses) {
386-
if (tryClass != null) {
387-
_getResultsForClass(
388-
tryClass, codeRefChomped, results, codeRef, packageGraph);
389-
}
390-
results.remove(null);
391-
if (results.isNotEmpty) break;
392-
}
393373

394-
if (results.isEmpty && realClass != null) {
395-
for (Class superClass
396-
in realClass.publicSuperChain.map((et) => et.element as Class)) {
397-
if (!tryClasses.contains(superClass)) {
398-
_getResultsForClass(
399-
superClass, codeRefChomped, results, codeRef, packageGraph);
400-
}
401-
results.remove(null);
402-
if (results.isNotEmpty) break;
403-
}
404-
}
405-
}
374+
// This could be local to the class, look there first.
375+
_findWithinTryClasses(results, preferredClass, element, codeRefChomped, codeRef, packageGraph);
406376
results.remove(null);
407377

408378
// We now need the ref element cache to keep from repeatedly searching [Package.allModelElements].
409379
// But if not, look for a fully qualified match. (That only makes sense
410380
// if the codeRef might be qualified, and contains periods.)
411-
if (results.isEmpty &&
412-
codeRefChomped.contains('.') &&
413-
packageGraph.findRefElementCache.containsKey(codeRefChomped)) {
414-
for (final ModelElement modelElement
415-
in packageGraph.findRefElementCache[codeRefChomped]) {
416-
if (!_ConsiderIfConstructor(codeRef, modelElement)) continue;
417-
// For fully qualified matches, the original preferredClass passed
418-
// might make no sense. Instead, use the enclosing class from the
419-
// element in [_findRefElementCache], because that element's enclosing
420-
// class will be preferred from [codeRefChomped]'s perspective.
421-
results.add(packageGraph.findCanonicalModelElementFor(
422-
modelElement.element,
423-
preferredClass: modelElement.enclosingElement is Class
424-
? modelElement.enclosingElement
425-
: null));
426-
}
427-
}
381+
_findWithinRefElementCache(results, codeRefChomped, packageGraph, codeRef);
428382
results.remove(null);
429383

430384
// Only look for partially qualified matches if we didn't find a fully qualified one.
431-
if (results.isEmpty) {
432-
for (final modelElement in library.allModelElements) {
433-
if (!_ConsiderIfConstructor(codeRef, modelElement)) continue;
434-
if (codeRefChomped == modelElement.fullyQualifiedNameWithoutLibrary) {
435-
results.add(packageGraph.findCanonicalModelElementFor(
436-
modelElement.element,
437-
preferredClass: preferredClass));
438-
}
439-
}
440-
}
385+
_findPartiallyQualifiedMatches(results, library, codeRef, codeRefChomped, packageGraph, preferredClass);
441386
results.remove(null);
442387

443388
// And if we still haven't found anything, just search the whole ball-of-wax.
444-
if (results.isEmpty &&
445-
packageGraph.findRefElementCache.containsKey(codeRefChomped)) {
446-
for (final modelElement
447-
in packageGraph.findRefElementCache[codeRefChomped]) {
448-
if (codeRefChomped == modelElement.fullyQualifiedNameWithoutLibrary ||
449-
(modelElement is Library &&
450-
codeRefChomped == modelElement.fullyQualifiedName)) {
451-
results.add(
452-
packageGraph.findCanonicalModelElementFor(modelElement.element));
453-
}
454-
}
455-
}
389+
_findGlobalWithinRefElementCache(results, packageGraph, codeRefChomped);
456390
results.remove(null);
457391

458392
// This could conceivably be a reference to an enum member. They don't show up in allModelElements.
459393
// TODO(jcollins-g): Put enum members in allModelElements with useful hrefs without blowing up other assumptions about what that means.
460394
// TODO(jcollins-g): This doesn't provide good warnings if an enum and class have the same name in different libraries in the same package. Fix that.
461-
if (results.isEmpty) {
462-
List<String> codeRefChompedParts = codeRefChomped.split('.');
463-
if (codeRefChompedParts.length >= 2) {
464-
String maybeEnumName = codeRefChompedParts
465-
.sublist(0, codeRefChompedParts.length - 1)
466-
.join('.');
467-
String maybeEnumMember = codeRefChompedParts.last;
468-
if (packageGraph.findRefElementCache.containsKey(maybeEnumName)) {
469-
for (final modelElement
470-
in packageGraph.findRefElementCache[maybeEnumName]) {
471-
if (modelElement is Enum) {
472-
if (modelElement.constants.any((e) => e.name == maybeEnumMember)) {
473-
results.add(modelElement);
474-
break;
475-
}
476-
}
477-
}
478-
}
479-
}
480-
}
395+
_findEnumReferences(results, codeRefChomped, packageGraph);
481396
results.remove(null);
482397

483398
if (results.length > 1) {
@@ -560,6 +475,117 @@ ModelElement _findRefElementInLibrary(String codeRef, Warnable element,
560475
return result;
561476
}
562477

478+
void _findEnumReferences(Set<ModelElement> results, String codeRefChomped, PackageGraph packageGraph) {
479+
if (results.isEmpty) {
480+
List<String> codeRefChompedParts = codeRefChomped.split('.');
481+
if (codeRefChompedParts.length >= 2) {
482+
String maybeEnumName = codeRefChompedParts
483+
.sublist(0, codeRefChompedParts.length - 1)
484+
.join('.');
485+
String maybeEnumMember = codeRefChompedParts.last;
486+
if (packageGraph.findRefElementCache.containsKey(maybeEnumName)) {
487+
for (final modelElement
488+
in packageGraph.findRefElementCache[maybeEnumName]) {
489+
if (modelElement is Enum) {
490+
if (modelElement.constants.any((e) => e.name == maybeEnumMember)) {
491+
results.add(modelElement);
492+
break;
493+
}
494+
}
495+
}
496+
}
497+
}
498+
}
499+
}
500+
501+
void _findGlobalWithinRefElementCache(Set<ModelElement> results, PackageGraph packageGraph, String codeRefChomped) {
502+
if (results.isEmpty &&
503+
packageGraph.findRefElementCache.containsKey(codeRefChomped)) {
504+
for (final modelElement
505+
in packageGraph.findRefElementCache[codeRefChomped]) {
506+
if (codeRefChomped == modelElement.fullyQualifiedNameWithoutLibrary ||
507+
(modelElement is Library &&
508+
codeRefChomped == modelElement.fullyQualifiedName)) {
509+
results.add(
510+
packageGraph.findCanonicalModelElementFor(modelElement.element));
511+
}
512+
}
513+
}
514+
}
515+
516+
void _findPartiallyQualifiedMatches(Set<ModelElement> results, Library library, String codeRef, String codeRefChomped, PackageGraph packageGraph, Class preferredClass) {
517+
// Only look for partially qualified matches if we didn't find a fully qualified one.
518+
if (results.isEmpty && library.modelElementsNameMap.containsKey(codeRefChomped)) {
519+
for (final modelElement in library.modelElementsNameMap[codeRefChomped]) {
520+
if (!_ConsiderIfConstructor(codeRef, modelElement)) continue;
521+
results.add(packageGraph.findCanonicalModelElementFor(
522+
modelElement.element,
523+
preferredClass: preferredClass));
524+
}
525+
}
526+
}
527+
528+
void _findWithinRefElementCache(Set<ModelElement> results, String codeRefChomped, PackageGraph packageGraph, String codeRef) {
529+
// We now need the ref element cache to keep from repeatedly searching [Package.allModelElements].
530+
// But if not, look for a fully qualified match. (That only makes sense
531+
// if the codeRef might be qualified, and contains periods.)
532+
if (results.isEmpty &&
533+
codeRefChomped.contains('.') &&
534+
packageGraph.findRefElementCache.containsKey(codeRefChomped)) {
535+
for (final ModelElement modelElement
536+
in packageGraph.findRefElementCache[codeRefChomped]) {
537+
if (!_ConsiderIfConstructor(codeRef, modelElement)) continue;
538+
// For fully qualified matches, the original preferredClass passed
539+
// might make no sense. Instead, use the enclosing class from the
540+
// element in [_findRefElementCache], because that element's enclosing
541+
// class will be preferred from [codeRefChomped]'s perspective.
542+
results.add(packageGraph.findCanonicalModelElementFor(
543+
modelElement.element,
544+
preferredClass: modelElement.enclosingElement is Class
545+
? modelElement.enclosingElement
546+
: null));
547+
}
548+
}
549+
}
550+
551+
void _findWithinTryClasses(Set<ModelElement> results, Class preferredClass, Warnable element, String codeRefChomped, String codeRef, PackageGraph packageGraph) {
552+
if (results.isEmpty) {
553+
// Maybe this is local to a class.
554+
// TODO(jcollins-g): tryClasses is a strict subset of the superclass chain. Optimize.
555+
List<Class> tryClasses = [preferredClass];
556+
Class realClass = tryClasses.first;
557+
if (element is Inheritable) {
558+
Inheritable overriddenElement = element.overriddenElement;
559+
while (overriddenElement != null) {
560+
tryClasses.add(
561+
(element.overriddenElement as EnclosedElement).enclosingElement);
562+
overriddenElement = overriddenElement.overriddenElement;
563+
}
564+
}
565+
566+
for (Class tryClass in tryClasses) {
567+
if (tryClass != null) {
568+
_getResultsForClass(
569+
tryClass, codeRefChomped, results, codeRef, packageGraph);
570+
}
571+
results.remove(null);
572+
if (results.isNotEmpty) break;
573+
}
574+
575+
if (results.isEmpty && realClass != null) {
576+
for (Class superClass
577+
in realClass.publicSuperChain.map((et) => et.element as Class)) {
578+
if (!tryClasses.contains(superClass)) {
579+
_getResultsForClass(
580+
superClass, codeRefChomped, results, codeRef, packageGraph);
581+
}
582+
results.remove(null);
583+
if (results.isNotEmpty) break;
584+
}
585+
}
586+
}
587+
}
588+
563589
// _getResultsForClass assumes codeRefChomped might be a member of tryClass (inherited or not)
564590
// and will add to [results]
565591
void _getResultsForClass(Class tryClass, String codeRefChomped,

lib/src/model.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2532,6 +2532,20 @@ class Library extends ModelElement with Categorization, TopLevelContainer {
25322532
return name;
25332533
}
25342534

2535+
Map<String, Set<ModelElement>> _modelElementsNameMap;
2536+
/// Map of [fullyQualifiedNameWithoutLibrary] to all matching [ModelElement]s
2537+
/// in this library. Used for code reference lookups.
2538+
Map<String, Set<ModelElement>> get modelElementsNameMap {
2539+
if (_modelElementsNameMap == null) {
2540+
_modelElementsNameMap = new Map<String, Set<ModelElement>>();
2541+
allModelElements.forEach((ModelElement modelElement) {
2542+
_modelElementsNameMap.putIfAbsent(modelElement.fullyQualifiedNameWithoutLibrary, () => new Set());
2543+
_modelElementsNameMap[modelElement.fullyQualifiedNameWithoutLibrary].add(modelElement);
2544+
});
2545+
}
2546+
return _modelElementsNameMap;
2547+
}
2548+
25352549
Map<Element, Set<ModelElement>> _modelElementsMap;
25362550
Map<Element, Set<ModelElement>> get modelElementsMap {
25372551
if (_modelElementsMap == null) {

0 commit comments

Comments
 (0)