| // Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| import 'package:analysis_server/src/computer/computer_overrides.dart'; | |
| import 'package:analyzer/dart/element/element.dart'; | |
| import 'package:analyzer/src/dartdoc/dartdoc_directive_info.dart'; | |
| /// Computes documentation for an [Element]. | |
| class DartDocumentationComputer { | |
| final DartdocDirectiveInfo dartdocInfo; | |
| new(this.dartdocInfo); | |
| Documentation? compute( | |
| Element elementBeingDocumented, { | |
| bool includeSummary = false, | |
| }) { | |
| var element = elementBeingDocumented.elementWithDocumentation; | |
| if (element == null) { | |
| // This can happen when the code is invalid, such as having an | |
| // initializing formal parameter for a field that does not exist. | |
| return null; | |
| } | |
| Element? documentedElement; | |
| Element? documentedGetter; | |
| // Look for documentation comments of overridden members | |
| var overridden = findOverriddenElements(element); | |
| var candidates = [ | |
| element, | |
| ...overridden.superElements, | |
| ...overridden.interfaceElements, | |
| if (element case PropertyAccessorElement(:var variable)) variable, | |
| ]; | |
| for (var candidate in candidates) { | |
| if (candidate.documentationComment != null) { | |
| documentedElement = candidate; | |
| break; | |
| } | |
| if (documentedGetter == null && candidate is SetterElement) { | |
| var getter = candidate.correspondingGetter; | |
| if (getter != null && getter.documentationComment != null) { | |
| documentedGetter = getter; | |
| } | |
| } | |
| } | |
| // Use documentation of a corresponding getter if setters don't have it | |
| documentedElement ??= documentedGetter; | |
| if (documentedElement == null) { | |
| return null; | |
| } | |
| var rawDoc = documentedElement.documentationComment; | |
| if (rawDoc == null) { | |
| return null; | |
| } | |
| var result = dartdocInfo.processDartdoc( | |
| rawDoc, | |
| includeSummary: includeSummary, | |
| ); | |
| var documentedElementClass = documentedElement.enclosingElement; | |
| if (documentedElementClass != null && | |
| documentedElementClass != element.enclosingElement) { | |
| var documentedClass = documentedElementClass.displayName; | |
| result.full = '${result.full}\n\nCopied from `$documentedClass`.'; | |
| } | |
| return result; | |
| } | |
| /// Compute documentation for [element] and return either the summary or full | |
| /// docs (or `null`) depending on `preference`. | |
| String? computePreferred( | |
| Element element, | |
| DocumentationPreference preference, | |
| ) { | |
| if (preference == DocumentationPreference.none) { | |
| return null; | |
| } | |
| var doc = compute( | |
| element, | |
| includeSummary: preference == DocumentationPreference.summary, | |
| ); | |
| return doc is DocumentationWithSummary ? doc.summary : doc?.full; | |
| } | |
| } | |
| /// The preferred kind of documentation for requests that might return many | |
| /// results and could have large payloads when docs are included | |
| enum DocumentationPreference { none, summary, full } | |
| extension on Element { | |
| /// The element whose documentation should be used when showing documentation | |
| /// for this element. | |
| Element? get elementWithDocumentation { | |
| var self = this; | |
| if (self is FieldFormalParameterElement) { | |
| return self.field; | |
| } else if (self is SuperFormalParameterElement) { | |
| // Treat a super formal parameter like a field formal parameter if it's | |
| // eventually assigned to a field, but as any other formal parameter if it | |
| // isn't. | |
| var superParameter = self.superConstructorParameter; | |
| while (superParameter is SuperFormalParameterElement) { | |
| superParameter = superParameter.superConstructorParameter; | |
| } | |
| if (superParameter is FieldFormalParameterElement) { | |
| return superParameter.field; | |
| } | |
| return self.enclosingElement; | |
| } else if (self is FormalParameterElement) { | |
| return self.enclosingElement; | |
| } | |
| return this; | |
| } | |
| } |