|
| 1 | +// Copyright 2013 The Flutter Authors. All rights reserved. |
| 2 | +// Use of this source code is governed by a BSD-style license that can be |
| 3 | +// found in the LICENSE file. |
| 4 | + |
| 5 | +import 'dart:async'; |
| 6 | +import 'dart:convert'; |
| 7 | +import 'dart:io' as io; |
| 8 | + |
| 9 | +import 'package:path/path.dart' as p; |
| 10 | + |
| 11 | +import '../logger.dart'; |
| 12 | +import 'command.dart'; |
| 13 | +import 'flags.dart'; |
| 14 | + |
| 15 | +/// The 'format' command. |
| 16 | +/// |
| 17 | +/// The format command implementation below works by spawning another Dart VM to |
| 18 | +/// run the program under ci/bin/format.dart. |
| 19 | +/// |
| 20 | +// TODO(team-engine): Part of https://github.com/flutter/flutter/issues/132807. |
| 21 | +// Instead, format.dart should be moved under the engine_tool package and |
| 22 | +// invoked by a function call. The file ci/bin/format.dart should be split up so |
| 23 | +// that each of its `FormatCheckers` is in a separate file under src/formatters, |
| 24 | +// and they should be unit-tested. |
| 25 | +final class FormatCommand extends CommandBase { |
| 26 | + // ignore: public_member_api_docs |
| 27 | + FormatCommand({ |
| 28 | + required super.environment, |
| 29 | + }) { |
| 30 | + argParser |
| 31 | + ..addFlag( |
| 32 | + allFlag, |
| 33 | + abbr: 'a', |
| 34 | + help: 'By default only dirty files are checked. This flag causes all ' |
| 35 | + 'files to be checked. (Slow)', |
| 36 | + negatable: false, |
| 37 | + ) |
| 38 | + ..addFlag( |
| 39 | + fixFlag, |
| 40 | + abbr: 'f', |
| 41 | + help: 'Updates the formatting of files in-place. --no-$fixFlag ' |
| 42 | + 'implies --$printDiffsFlag.', |
| 43 | + defaultsTo: true, |
| 44 | + ) |
| 45 | + ..addFlag( |
| 46 | + printDiffsFlag, |
| 47 | + abbr: 'p', |
| 48 | + help: 'Output diffs that fix formatting errors to the logger. Implies ' |
| 49 | + '--no-$fixFlag.', |
| 50 | + negatable: false, |
| 51 | + ) |
| 52 | + ..addFlag( |
| 53 | + quietFlag, |
| 54 | + abbr: 'q', |
| 55 | + help: 'Silences all log messages except for errors and warnings', |
| 56 | + negatable: false, |
| 57 | + ) |
| 58 | + ..addFlag( |
| 59 | + verboseFlag, |
| 60 | + abbr: 'v', |
| 61 | + help: 'Prints verbose output', |
| 62 | + negatable: false, |
| 63 | + ); |
| 64 | + } |
| 65 | + |
| 66 | + @override |
| 67 | + String get name => 'format'; |
| 68 | + |
| 69 | + @override |
| 70 | + String get description => 'Formats files using standard formatters and styles.'; |
| 71 | + |
| 72 | + @override |
| 73 | + Future<int> run() async { |
| 74 | + final bool all = argResults![allFlag]! as bool; |
| 75 | + final bool rawPrintDiffs = argResults![printDiffsFlag]! as bool; |
| 76 | + final bool rawFix = argResults![fixFlag]! as bool; |
| 77 | + final bool quiet = argResults![quietFlag]! as bool; |
| 78 | + final bool verbose = argResults![verboseFlag]! as bool; |
| 79 | + // If --print-diffs was passed, disable fixing. |
| 80 | + final bool fix = rawFix && !rawPrintDiffs; |
| 81 | + // If --no-fix was passed, then enable diff printing. |
| 82 | + final bool printDiffs = rawPrintDiffs || !rawFix; |
| 83 | + final String formatPath = p.join( |
| 84 | + environment.engine.flutterDir.path, 'ci', 'bin', 'format.dart', |
| 85 | + ); |
| 86 | + |
| 87 | + final io.Process process = await environment.processRunner.processManager.start( |
| 88 | + <String>[ |
| 89 | + environment.platform.resolvedExecutable, |
| 90 | + formatPath, |
| 91 | + if (all) '--all-files', |
| 92 | + if (fix) '--fix', |
| 93 | + if (verbose) '--verbose', |
| 94 | + ], |
| 95 | + workingDirectory: environment.engine.flutterDir.path, |
| 96 | + ); |
| 97 | + final Completer<void> stdoutComplete = Completer<void>(); |
| 98 | + final Completer<void> stderrComplete = Completer<void>(); |
| 99 | + |
| 100 | + final _FormatStreamer streamer = _FormatStreamer( |
| 101 | + environment.logger, |
| 102 | + printDiffs, |
| 103 | + quiet, |
| 104 | + ); |
| 105 | + process.stdout |
| 106 | + .transform<String>(const Utf8Decoder()) |
| 107 | + .transform(const LineSplitter()) |
| 108 | + .listen( |
| 109 | + streamer.nextStdout, |
| 110 | + onDone: () async => stdoutComplete.complete(), |
| 111 | + ); |
| 112 | + process.stderr |
| 113 | + .transform<String>(const Utf8Decoder()) |
| 114 | + .transform(const LineSplitter()) |
| 115 | + .listen( |
| 116 | + streamer.nextStderr, |
| 117 | + onDone: () async => stderrComplete.complete(), |
| 118 | + ); |
| 119 | + |
| 120 | + await Future.wait<void>(<Future<void>>[ |
| 121 | + stdoutComplete.future, stderrComplete.future, |
| 122 | + ]); |
| 123 | + final int exitCode = await process.exitCode; |
| 124 | + |
| 125 | + return exitCode; |
| 126 | + } |
| 127 | +} |
| 128 | + |
| 129 | +class _FormatStreamer { |
| 130 | + _FormatStreamer(this.logger, this.printDiffs, this.quiet); |
| 131 | + |
| 132 | + final Logger logger; |
| 133 | + final bool printDiffs; |
| 134 | + final bool quiet; |
| 135 | + |
| 136 | + bool inADiff = false; |
| 137 | + bool inProgress = false; |
| 138 | + |
| 139 | + void nextStdout(String line) { |
| 140 | + if (quiet) { |
| 141 | + return; |
| 142 | + } |
| 143 | + final String l = line.trim(); |
| 144 | + if (l == 'To fix, run `et format` or:') { |
| 145 | + inADiff = true; |
| 146 | + } |
| 147 | + if (l.isNotEmpty && (!inADiff || printDiffs)) { |
| 148 | + if (_isProgressLine(l)) { |
| 149 | + inProgress = true; |
| 150 | + logger.clearLine(); |
| 151 | + logger.status('$l\r', newline: false); |
| 152 | + } else { |
| 153 | + if (inProgress) { |
| 154 | + logger.clearLine(); |
| 155 | + inProgress = false; |
| 156 | + } |
| 157 | + logger.status(l); |
| 158 | + } |
| 159 | + } |
| 160 | + if (l == 'DONE') { |
| 161 | + inADiff = false; |
| 162 | + } |
| 163 | + } |
| 164 | + |
| 165 | + void nextStderr(String line) { |
| 166 | + final String l = line.trim(); |
| 167 | + if (l.isEmpty) { |
| 168 | + return; |
| 169 | + } |
| 170 | + logger.error(l); |
| 171 | + } |
| 172 | + |
| 173 | + bool _isProgressLine(String l) { |
| 174 | + final List<String> words = l.split(','); |
| 175 | + return words.isNotEmpty && words[0].endsWith('% done'); |
| 176 | + } |
| 177 | +} |
0 commit comments