mirror of
https://github.com/flutter/flutter.git
synced 2026-02-15 23:33:36 +08:00
This auto-formats all *.dart files in the repository outside of the `engine` subdirectory and enforces that these files stay formatted with a presubmit check. **Reviewers:** Please carefully review all the commits except for the one titled "formatted". The "formatted" commit was auto-generated by running `dev/tools/format.sh -a -f`. The other commits were hand-crafted to prepare the repo for the formatting change. I recommend reviewing the commits one-by-one via the "Commits" tab and avoiding Github's "Files changed" tab as it will likely slow down your browser because of the size of this PR. --------- Co-authored-by: Kate Lovett <katelovett@google.com> Co-authored-by: LongCatIsLooong <31859944+LongCatIsLooong@users.noreply.github.com>
351 lines
12 KiB
Dart
351 lines
12 KiB
Dart
// Copyright 2014 The Flutter Authors. 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:analyzer/dart/analysis/features.dart';
|
|
import 'package:analyzer/dart/analysis/results.dart';
|
|
import 'package:analyzer/dart/analysis/utilities.dart';
|
|
import 'package:analyzer/dart/ast/ast.dart';
|
|
import 'package:analyzer/dart/ast/token.dart';
|
|
import 'package:analyzer/dart/ast/visitor.dart';
|
|
import 'package:analyzer/file_system/file_system.dart' as afs;
|
|
import 'package:analyzer/file_system/physical_file_system.dart' as afs;
|
|
import 'package:analyzer/source/line_info.dart';
|
|
import 'package:file/file.dart';
|
|
|
|
import 'data_types.dart';
|
|
import 'util.dart';
|
|
|
|
/// Gets an iterable over all of the blocks of documentation comments in a file
|
|
/// using the analyzer.
|
|
///
|
|
/// Each entry in the list is a list of source lines corresponding to the
|
|
/// documentation comment block.
|
|
Iterable<List<SourceLine>> getFileDocumentationComments(File file) {
|
|
return getDocumentationComments(getFileElements(file));
|
|
}
|
|
|
|
/// Gets an iterable over all of the blocks of documentation comments from an
|
|
/// iterable over the [SourceElement]s involved.
|
|
Iterable<List<SourceLine>> getDocumentationComments(Iterable<SourceElement> elements) {
|
|
return elements
|
|
.where((SourceElement element) => element.comment.isNotEmpty)
|
|
.map<List<SourceLine>>((SourceElement element) => element.comment);
|
|
}
|
|
|
|
/// Gets an iterable over the comment [SourceElement]s in a file.
|
|
Iterable<SourceElement> getFileCommentElements(File file) {
|
|
return getCommentElements(getFileElements(file));
|
|
}
|
|
|
|
/// Filters the source `elements` to only return the comment elements.
|
|
Iterable<SourceElement> getCommentElements(Iterable<SourceElement> elements) {
|
|
return elements.where((SourceElement element) => element.comment.isNotEmpty);
|
|
}
|
|
|
|
/// Reads the file content from a string, to avoid having to read the file more
|
|
/// than once if the caller already has the content in memory.
|
|
///
|
|
/// The `file` argument is used to tag the lines with a filename that they came from.
|
|
Iterable<SourceElement> getElementsFromString(String content, File file) {
|
|
final ParseStringResult parseResult = parseString(
|
|
featureSet: FeatureSet.fromEnableFlags2(
|
|
sdkLanguageVersion: FlutterInformation.instance.getDartSdkVersion(),
|
|
flags: <String>[],
|
|
),
|
|
content: content,
|
|
);
|
|
final _SourceVisitor<CompilationUnit> visitor = _SourceVisitor<CompilationUnit>(file);
|
|
visitor.visitCompilationUnit(parseResult.unit);
|
|
visitor.assignLineNumbers();
|
|
return visitor.elements;
|
|
}
|
|
|
|
/// Gets an iterable over the [SourceElement]s in the given `file`.
|
|
///
|
|
/// Takes an optional [ResourceProvider] to allow reading from a memory
|
|
/// filesystem.
|
|
Iterable<SourceElement> getFileElements(File file, {afs.ResourceProvider? resourceProvider}) {
|
|
resourceProvider ??= afs.PhysicalResourceProvider.INSTANCE;
|
|
final ParseStringResult parseResult = parseFile(
|
|
featureSet: FeatureSet.fromEnableFlags2(
|
|
sdkLanguageVersion: FlutterInformation.instance.getDartSdkVersion(),
|
|
flags: <String>[],
|
|
),
|
|
path: file.absolute.path,
|
|
resourceProvider: resourceProvider,
|
|
);
|
|
final _SourceVisitor<CompilationUnit> visitor = _SourceVisitor<CompilationUnit>(file);
|
|
visitor.visitCompilationUnit(parseResult.unit);
|
|
visitor.assignLineNumbers();
|
|
return visitor.elements;
|
|
}
|
|
|
|
class _SourceVisitor<T> extends RecursiveAstVisitor<T> {
|
|
_SourceVisitor(this.file) : elements = <SourceElement>{};
|
|
|
|
final Set<SourceElement> elements;
|
|
String enclosingClass = '';
|
|
|
|
File file;
|
|
|
|
void assignLineNumbers() {
|
|
final String contents = file.readAsStringSync();
|
|
final LineInfo lineInfo = LineInfo.fromContent(contents);
|
|
|
|
final Set<SourceElement> removedElements = <SourceElement>{};
|
|
final Set<SourceElement> replacedElements = <SourceElement>{};
|
|
for (final SourceElement element in elements) {
|
|
final List<SourceLine> newLines = <SourceLine>[];
|
|
for (final SourceLine line in element.comment) {
|
|
final CharacterLocation intervalLine = lineInfo.getLocation(line.startChar);
|
|
newLines.add(line.copyWith(line: intervalLine.lineNumber));
|
|
}
|
|
final int elementLine = lineInfo.getLocation(element.startPos).lineNumber;
|
|
replacedElements.add(element.copyWith(comment: newLines, startLine: elementLine));
|
|
removedElements.add(element);
|
|
}
|
|
elements.removeAll(removedElements);
|
|
elements.addAll(replacedElements);
|
|
}
|
|
|
|
List<SourceLine> _processComment(String element, Comment comment) {
|
|
final List<SourceLine> result = <SourceLine>[];
|
|
if (comment.tokens.isNotEmpty) {
|
|
for (final Token token in comment.tokens) {
|
|
result.add(
|
|
SourceLine(
|
|
token.toString(),
|
|
element: element,
|
|
file: file,
|
|
startChar: token.charOffset,
|
|
endChar: token.charEnd,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@override
|
|
T? visitCompilationUnit(CompilationUnit node) {
|
|
elements.clear();
|
|
return super.visitCompilationUnit(node);
|
|
}
|
|
|
|
static bool isPublic(String name) {
|
|
return !name.startsWith('_');
|
|
}
|
|
|
|
static bool isInsideMethod(AstNode startNode) {
|
|
AstNode? node = startNode.parent;
|
|
while (node != null) {
|
|
if (node is MethodDeclaration) {
|
|
return true;
|
|
}
|
|
node = node.parent;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@override
|
|
T? visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
|
|
for (final VariableDeclaration declaration in node.variables.variables) {
|
|
if (!isPublic(declaration.name.lexeme)) {
|
|
continue;
|
|
}
|
|
List<SourceLine> comment = <SourceLine>[];
|
|
if (node.documentationComment != null && node.documentationComment!.tokens.isNotEmpty) {
|
|
comment = _processComment(declaration.name.lexeme, node.documentationComment!);
|
|
}
|
|
elements.add(
|
|
SourceElement(
|
|
SourceElementType.topLevelVariableType,
|
|
declaration.name.lexeme,
|
|
node.beginToken.charOffset,
|
|
file: file,
|
|
className: enclosingClass,
|
|
comment: comment,
|
|
),
|
|
);
|
|
}
|
|
return super.visitTopLevelVariableDeclaration(node);
|
|
}
|
|
|
|
@override
|
|
T? visitGenericTypeAlias(GenericTypeAlias node) {
|
|
if (isPublic(node.name.lexeme)) {
|
|
List<SourceLine> comment = <SourceLine>[];
|
|
if (node.documentationComment != null && node.documentationComment!.tokens.isNotEmpty) {
|
|
comment = _processComment(node.name.lexeme, node.documentationComment!);
|
|
}
|
|
elements.add(
|
|
SourceElement(
|
|
SourceElementType.typedefType,
|
|
node.name.lexeme,
|
|
node.beginToken.charOffset,
|
|
file: file,
|
|
comment: comment,
|
|
),
|
|
);
|
|
}
|
|
return super.visitGenericTypeAlias(node);
|
|
}
|
|
|
|
@override
|
|
T? visitFieldDeclaration(FieldDeclaration node) {
|
|
for (final VariableDeclaration declaration in node.fields.variables) {
|
|
if (!isPublic(declaration.name.lexeme) || !isPublic(enclosingClass)) {
|
|
continue;
|
|
}
|
|
List<SourceLine> comment = <SourceLine>[];
|
|
if (node.documentationComment != null && node.documentationComment!.tokens.isNotEmpty) {
|
|
assert(enclosingClass.isNotEmpty);
|
|
comment = _processComment(
|
|
'$enclosingClass.${declaration.name.lexeme}',
|
|
node.documentationComment!,
|
|
);
|
|
}
|
|
elements.add(
|
|
SourceElement(
|
|
SourceElementType.fieldType,
|
|
declaration.name.lexeme,
|
|
node.beginToken.charOffset,
|
|
file: file,
|
|
className: enclosingClass,
|
|
comment: comment,
|
|
override: _isOverridden(node),
|
|
),
|
|
);
|
|
return super.visitFieldDeclaration(node);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
T? visitConstructorDeclaration(ConstructorDeclaration node) {
|
|
final String fullName = '$enclosingClass${node.name == null ? '' : '.${node.name}'}';
|
|
if (isPublic(enclosingClass) && (node.name == null || isPublic(node.name!.lexeme))) {
|
|
List<SourceLine> comment = <SourceLine>[];
|
|
if (node.documentationComment != null && node.documentationComment!.tokens.isNotEmpty) {
|
|
comment = _processComment('$enclosingClass.$fullName', node.documentationComment!);
|
|
}
|
|
elements.add(
|
|
SourceElement(
|
|
SourceElementType.constructorType,
|
|
fullName,
|
|
node.beginToken.charOffset,
|
|
file: file,
|
|
className: enclosingClass,
|
|
comment: comment,
|
|
),
|
|
);
|
|
}
|
|
return super.visitConstructorDeclaration(node);
|
|
}
|
|
|
|
@override
|
|
T? visitFunctionDeclaration(FunctionDeclaration node) {
|
|
if (isPublic(node.name.lexeme)) {
|
|
List<SourceLine> comment = <SourceLine>[];
|
|
// Skip functions that are defined inside of methods.
|
|
if (!isInsideMethod(node)) {
|
|
if (node.documentationComment != null && node.documentationComment!.tokens.isNotEmpty) {
|
|
comment = _processComment(node.name.lexeme, node.documentationComment!);
|
|
}
|
|
elements.add(
|
|
SourceElement(
|
|
SourceElementType.functionType,
|
|
node.name.lexeme,
|
|
node.beginToken.charOffset,
|
|
file: file,
|
|
comment: comment,
|
|
override: _isOverridden(node),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
return super.visitFunctionDeclaration(node);
|
|
}
|
|
|
|
@override
|
|
T? visitMethodDeclaration(MethodDeclaration node) {
|
|
if (isPublic(node.name.lexeme) && isPublic(enclosingClass)) {
|
|
List<SourceLine> comment = <SourceLine>[];
|
|
if (node.documentationComment != null && node.documentationComment!.tokens.isNotEmpty) {
|
|
assert(enclosingClass.isNotEmpty);
|
|
comment = _processComment(
|
|
'$enclosingClass.${node.name.lexeme}',
|
|
node.documentationComment!,
|
|
);
|
|
}
|
|
elements.add(
|
|
SourceElement(
|
|
SourceElementType.methodType,
|
|
node.name.lexeme,
|
|
node.beginToken.charOffset,
|
|
file: file,
|
|
className: enclosingClass,
|
|
comment: comment,
|
|
override: _isOverridden(node),
|
|
),
|
|
);
|
|
}
|
|
return super.visitMethodDeclaration(node);
|
|
}
|
|
|
|
bool _isOverridden(AnnotatedNode node) {
|
|
return node.metadata.where((Annotation annotation) {
|
|
return annotation.name.name == 'override';
|
|
}).isNotEmpty;
|
|
}
|
|
|
|
@override
|
|
T? visitMixinDeclaration(MixinDeclaration node) {
|
|
enclosingClass = node.name.lexeme;
|
|
if (!node.name.lexeme.startsWith('_')) {
|
|
enclosingClass = node.name.lexeme;
|
|
List<SourceLine> comment = <SourceLine>[];
|
|
if (node.documentationComment != null && node.documentationComment!.tokens.isNotEmpty) {
|
|
comment = _processComment(node.name.lexeme, node.documentationComment!);
|
|
}
|
|
elements.add(
|
|
SourceElement(
|
|
SourceElementType.classType,
|
|
node.name.lexeme,
|
|
node.beginToken.charOffset,
|
|
file: file,
|
|
comment: comment,
|
|
),
|
|
);
|
|
}
|
|
final T? result = super.visitMixinDeclaration(node);
|
|
enclosingClass = '';
|
|
return result;
|
|
}
|
|
|
|
@override
|
|
T? visitClassDeclaration(ClassDeclaration node) {
|
|
enclosingClass = node.name.lexeme;
|
|
if (!node.name.lexeme.startsWith('_')) {
|
|
enclosingClass = node.name.lexeme;
|
|
List<SourceLine> comment = <SourceLine>[];
|
|
if (node.documentationComment != null && node.documentationComment!.tokens.isNotEmpty) {
|
|
comment = _processComment(node.name.lexeme, node.documentationComment!);
|
|
}
|
|
elements.add(
|
|
SourceElement(
|
|
SourceElementType.classType,
|
|
node.name.lexeme,
|
|
node.beginToken.charOffset,
|
|
file: file,
|
|
comment: comment,
|
|
),
|
|
);
|
|
}
|
|
final T? result = super.visitClassDeclaration(node);
|
|
enclosingClass = '';
|
|
return result;
|
|
}
|
|
}
|