blob: a6c3c2ececf7045973da564220b60207e85a60bf [file] [log] [blame]
// 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 '../base/common.dart';
import '../base/file_system.dart';
import '../base/io.dart';
import '../base/logger.dart';
import '../dart/analysis.dart';
import 'analyze_base.dart';
class AnalyzeContinuously extends AnalyzeBase {
AnalyzeContinuously(
super.argResults,
List<Directory> repoPackages, {
required super.fileSystem,
required super.logger,
required super.terminal,
required super.platform,
required super.processManager,
required super.artifacts,
required super.suppressAnalytics,
}) : super(
repoPackages: repoPackages,
);
String? analysisTarget;
bool firstAnalysis = true;
Set<String> analyzedPaths = <String>{};
Map<String, List<AnalysisError>> analysisErrors = <String, List<AnalysisError>>{};
final Stopwatch analysisTimer = Stopwatch();
int lastErrorCount = 0;
Status? analysisStatus;
@override
Future<void> analyze() async {
List<String> directories;
if (isFlutterRepo) {
final PackageDependencyTracker dependencies = PackageDependencyTracker();
dependencies.checkForConflictingDependencies(repoPackages, dependencies);
directories = <String>[flutterRoot];
analysisTarget = 'Flutter repository';
logger.printTrace('Analyzing Flutter repository:');
} else {
directories = <String>[fileSystem.currentDirectory.path];
analysisTarget = fileSystem.currentDirectory.path;
}
final AnalysisServer server = AnalysisServer(
sdkPath,
directories,
fileSystem: fileSystem,
logger: logger,
platform: platform,
processManager: processManager,
terminal: terminal,
protocolTrafficLog: protocolTrafficLog,
suppressAnalytics: suppressAnalytics,
);
server.onAnalyzing.listen((bool isAnalyzing) => _handleAnalysisStatus(server, isAnalyzing));
server.onErrors.listen(_handleAnalysisErrors);
await server.start();
final int? exitCode = await server.onExit;
final String message = 'Analysis server exited with code $exitCode.';
if (exitCode != 0) {
throwToolExit(message, exitCode: exitCode);
}
logger.printStatus(message);
if (server.didServerErrorOccur) {
throwToolExit('Server error(s) occurred.');
}
}
void _handleAnalysisStatus(AnalysisServer server, bool isAnalyzing) {
if (isAnalyzing) {
analysisStatus?.cancel();
if (!firstAnalysis) {
logger.printStatus('\n');
}
analysisStatus = logger.startProgress('Analyzing $analysisTarget...');
analyzedPaths.clear();
analysisTimer.start();
} else {
analysisStatus?.stop();
analysisStatus = null;
analysisTimer.stop();
logger.printStatus(terminal.clearScreen(), newline: false);
// Remove errors for deleted files, sort, and print errors.
final List<AnalysisError> sortedErrors = <AnalysisError>[];
final List<String> pathsToRemove = <String>[];
analysisErrors.forEach((String path, List<AnalysisError> errors) {
if (fileSystem.isFileSync(path)) {
sortedErrors.addAll(errors);
} else {
pathsToRemove.add(path);
}
});
analysisErrors.removeWhere((String path, _) => pathsToRemove.contains(path));
sortedErrors.sort();
for (final AnalysisError error in sortedErrors) {
logger.printStatus(error.toString());
logger.printTrace('error code: ${error.code}');
}
dumpErrors(sortedErrors.map<String>((AnalysisError error) => error.toLegacyString()));
final int issueCount = sortedErrors.length;
final int issueDiff = issueCount - lastErrorCount;
lastErrorCount = issueCount;
final String seconds = (analysisTimer.elapsedMilliseconds / 1000.0).toStringAsFixed(2);
final String errorsMessage = AnalyzeBase.generateErrorsMessage(
issueCount: issueCount,
issueDiff: issueDiff,
files: analyzedPaths.length,
seconds: seconds,
);
logger.printStatus(errorsMessage);
if (firstAnalysis && isBenchmarking) {
writeBenchmark(analysisTimer, issueCount);
server.dispose().whenComplete(() { exit(issueCount > 0 ? 1 : 0); });
}
firstAnalysis = false;
}
}
void _handleAnalysisErrors(FileAnalysisErrors fileErrors) {
fileErrors.errors.removeWhere((AnalysisError error) => error.type == 'TODO');
analyzedPaths.add(fileErrors.file);
analysisErrors[fileErrors.file] = fileErrors.errors;
}
}