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