blob: 18e2419c30bae03a2fe2e8dbbac8eacc2d896a2d [file] [log] [blame]
// 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:analysis_server/src/utilities/extensions/element.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;
DartDocumentationComputer(this.dartdocInfo);
Documentation? compute(
Element elementBeingDocumented, {
bool includeSummary = false,
}) {
var element = switch (elementBeingDocumented) {
FieldFormalParameterElement() => elementBeingDocumented.field2,
FormalParameterElement() => elementBeingDocumented.enclosingElement2,
_ => elementBeingDocumented,
};
if (element == null) {
// This can happen when the code is invalid, such as having a field 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(variable3: var variable?))
variable,
];
for (var candidate in candidates) {
if (candidate.documentationCommentOrNull != null) {
documentedElement = candidate;
break;
}
if (documentedGetter == null && candidate is SetterElement) {
var getter = candidate.correspondingGetter2;
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.documentationCommentOrNull;
if (rawDoc == null) {
return null;
}
var result = dartdocInfo.processDartdoc(
rawDoc,
includeSummary: includeSummary,
);
var documentedElementClass = documentedElement.enclosingElement2;
if (documentedElementClass != null &&
documentedElementClass != element.enclosingElement2) {
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 type of documentation the user prefers to see in hovers and other
/// related displays in their editor.
enum DocumentationPreference { none, summary, full }