Temporary branch to try formatting in isolates.
This uses Isolate.run() to run the formatting logic on separate
isolates. It also uses a Pool to ensure we don't try to load too many
files in parallel and run out of descriptors.
diff --git a/lib/src/cli/format_command.dart b/lib/src/cli/format_command.dart
index 99a8135..3dd2d7c 100644
--- a/lib/src/cli/format_command.dart
+++ b/lib/src/cli/format_command.dart
@@ -5,6 +5,7 @@
import 'package:args/command_runner.dart';
+import '../format_service.dart';
import '../io.dart';
import '../short/style_fix.dart';
import 'formatter_options.dart';
@@ -153,7 +154,10 @@
if (argResults.rest.isEmpty) {
await formatStdin(options, selection, stdinName);
} else {
- formatPaths(options, argResults.rest);
+ var service = FormatService(options);
+ await service.format(argResults.rest);
+
+ // await formatPaths(options, argResults.rest);
options.summary.show();
}
diff --git a/lib/src/format_service.dart b/lib/src/format_service.dart
new file mode 100644
index 0000000..6b3b2bf
--- /dev/null
+++ b/lib/src/format_service.dart
@@ -0,0 +1,144 @@
+// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+import 'dart:async';
+import 'dart:io';
+import 'dart:isolate';
+
+import 'package:path/path.dart' as p;
+import 'package:pool/pool.dart';
+
+import 'cli/formatter_options.dart';
+import 'dart_formatter.dart';
+import 'exceptions.dart';
+import 'profile.dart';
+import 'source_code.dart';
+
+class FormatService {
+ final FormatterOptions _options;
+
+ FormatService(this._options);
+
+ /// Formats all of the files and directories given by [paths].
+ Future<void> format(List<String> paths) async {
+ Profile.begin('FormatService.format()');
+
+ Profile.begin('list paths');
+ var files = <File>[];
+ for (var path in paths) {
+ var directory = Directory(path);
+ if (await directory.exists()) {
+ files.addAll(await _listDirectory(directory));
+ continue;
+ }
+
+ var file = File(path);
+ if (await file.exists()) {
+ files.add(file);
+ } else {
+ stderr.writeln('No file or directory found at "$path".');
+ }
+ }
+ Profile.end('list paths');
+
+ // TODO: Tune?
+ var pool = Pool(Platform.numberOfProcessors);
+ for (var file in files) {
+ unawaited(pool.withResource(() async {
+ await _format(file);
+ }));
+ }
+
+ await pool.close();
+
+ Profile.end('FormatService.format()');
+ Profile.report();
+ }
+
+ Future<List<File>> _listDirectory(Directory directory) async {
+ var files = <File>[];
+ _options.showDirectory(directory.path);
+
+ var shownHiddenPaths = <String>{};
+
+ var entries =
+ directory.listSync(recursive: true, followLinks: _options.followLinks);
+ entries.sort((a, b) => a.path.compareTo(b.path));
+
+ for (var entry in entries) {
+ var displayPath = _options.show.displayPath(directory.path, entry.path);
+
+ if (entry is Link) {
+ _options.showSkippedLink(displayPath);
+ continue;
+ }
+
+ if (entry is! File || !entry.path.endsWith('.dart')) continue;
+
+ // If the path is in a subdirectory starting with ".", ignore it.
+ var parts = p.split(p.relative(entry.path, from: directory.path));
+ int? hiddenIndex;
+ for (var i = 0; i < parts.length; i++) {
+ if (parts[i].startsWith('.')) {
+ hiddenIndex = i;
+ break;
+ }
+ }
+
+ if (hiddenIndex != null) {
+ // Since we'll hide everything inside the directory starting with ".",
+ // show the directory name once instead of once for each file.
+ var hiddenPath = p.joinAll(parts.take(hiddenIndex + 1));
+ if (shownHiddenPaths.add(hiddenPath)) {
+ _options.showHiddenPath(hiddenPath);
+ }
+ continue;
+ }
+
+ files.add(entry);
+ }
+
+ return files;
+ }
+
+ Future<void> _format(File file) async {
+ // TODO: Preserve old logic.
+ var displayPath = file.path;
+
+ var formatter = DartFormatter(
+ indent: _options.indent,
+ pageWidth: _options.pageWidth,
+ fixes: _options.fixes,
+ experimentFlags: _options.experimentFlags);
+
+ try {
+ var source = SourceCode(await file.readAsString(), uri: file.path);
+ _options.beforeFile(file, displayPath);
+
+ var output = await Isolate.run(() async {
+ return formatter.formatSource(source);
+ });
+
+ _options.afterFile(file, displayPath, output,
+ changed: source.text != output.text);
+ return;
+ } on FormatterException catch (err) {
+ var color = Platform.operatingSystem != 'windows' &&
+ stdioType(stderr) == StdioType.terminal;
+
+ stderr.writeln(err.message(color: color));
+ } on UnexpectedOutputException catch (err) {
+ stderr.writeln('''Hit a bug in the formatter when formatting $displayPath.
+$err
+Please report at github.com/dart-lang/dart_style/issues.''');
+ } catch (err, stack) {
+ stderr.writeln('''Hit a bug in the formatter when formatting $displayPath.
+Please report at github.com/dart-lang/dart_style/issues.
+$err
+$stack''');
+ }
+
+ // If we get here, some error occurred.
+ exitCode = 65;
+ }
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index 9aa6af0..c0fde10 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -13,6 +13,7 @@
args: ">=1.0.0 <3.0.0"
collection: "^1.17.0"
path: ^1.0.0
+ pool: ^1.5.1
pub_semver: ">=1.4.4 <3.0.0"
source_span: ^1.4.0