| // 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; |
| } |
| } |