| // 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/logger.dart'; | |
| import '../base/process.dart'; | |
| import '../dart/analysis.dart'; | |
| import 'analyze_base.dart'; | |
| class AnalyzeContinuously extends AnalyzeBase { | |
| AnalyzeContinuously( | |
| super.argResults, | |
| List<Directory> repoPackages, { | |
| required super.artifacts, | |
| required super.fileSystem, | |
| required super.logger, | |
| required super.platform, | |
| required super.processManager, | |
| required this.shutdownHooks, | |
| required super.suppressAnalytics, | |
| required super.terminal, | |
| }) : super(repoPackages: repoPackages); | |
| final ShutdownHooks shutdownHooks; | |
| String? analysisTarget; | |
| bool firstAnalysis = true; | |
| Set<String> analyzedPaths = <String>{}; | |
| Map<String, List<AnalysisError>> analysisErrors = <String, List<AnalysisError>>{}; | |
| final analysisTimer = Stopwatch(); | |
| int lastErrorCount = 0; | |
| Status? analysisStatus; | |
| @override | |
| Future<void> analyze() async { | |
| List<String> directories; | |
| if (isFlutterRepo) { | |
| final dependencies = PackageDependencyTracker(); | |
| dependencies.checkForConflictingDependencies(repoPackages, fileSystem: fileSystem); | |
| directories = <String>[flutterRoot]; | |
| analysisTarget = 'Flutter repository'; | |
| logger.printTrace('Analyzing Flutter repository:'); | |
| } else { | |
| directories = <String>[fileSystem.currentDirectory.path]; | |
| analysisTarget = fileSystem.currentDirectory.path; | |
| } | |
| final server = AnalysisServer( | |
| sdkPath, | |
| directories, | |
| fileSystem: fileSystem, | |
| logger: logger, | |
| platform: platform, | |
| processManager: processManager, | |
| terminal: terminal, | |
| protocolTrafficLog: protocolTrafficLog, | |
| suppressAnalytics: suppressAnalytics, | |
| usePlugins: usePlugins, | |
| ); | |
| server.onAnalyzing.listen((bool isAnalyzing) => _handleAnalysisStatus(server, isAnalyzing)); | |
| server.onErrors.listen(_handleAnalysisErrors); | |
| await server.start(); | |
| final int? exitCode = await server.onExit; | |
| final 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 sortedErrors = <AnalysisError>[]; | |
| final 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 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(() { | |
| exitWithHooks(issueCount > 0 ? 1 : 0, shutdownHooks: shutdownHooks); | |
| }); | |
| } | |
| firstAnalysis = false; | |
| } | |
| } | |
| void _handleAnalysisErrors(FileAnalysisErrors fileErrors) { | |
| analyzedPaths.add(fileErrors.file); | |
| analysisErrors[fileErrors.file] = fileErrors.errors; | |
| } | |
| } |