Skip to content

Commit e73b364

Browse files
[tool] Add optional swift-format support (flutter#5204)
Adds support for swift-format in the `format` command. For now this is optional, so is only triggered if the flag is explictly passed. In the future, once we have CI support for swift-format, we will likely want to make it on-by-default as with the other formatters. Part of flutter#41129
1 parent 4bf5114 commit e73b364

2 files changed

Lines changed: 97 additions & 6 deletions

File tree

script/tool/lib/src/format_command.dart

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const int _exitFlutterFormatFailed = 4;
3131
const int _exitJavaFormatFailed = 5;
3232
const int _exitGitFailed = 6;
3333
const int _exitDependencyMissing = 7;
34+
const int _exitSwiftFormatFailed = 8;
3435

3536
final Uri _googleFormatterUrl = Uri.https('github.com',
3637
'/google/google-java-format/releases/download/google-java-format-1.3/google-java-format-1.3-all-deps.jar');
@@ -44,18 +45,25 @@ class FormatCommand extends PackageCommand {
4445
super.platform,
4546
}) {
4647
argParser.addFlag('fail-on-change', hide: true);
47-
argParser.addOption('clang-format',
48+
argParser.addOption(_clangFormatArg,
4849
defaultsTo: 'clang-format', help: 'Path to "clang-format" executable.');
49-
argParser.addOption('java',
50+
argParser.addOption(_javaArg,
5051
defaultsTo: 'java', help: 'Path to "java" executable.');
52+
argParser.addOption(_swiftFormatArg,
53+
help: 'Path to "swift-format" executable.');
5154
}
5255

56+
static const String _clangFormatArg = 'clang-format';
57+
static const String _javaArg = 'java';
58+
static const String _swiftFormatArg = 'swift-format';
59+
5360
@override
5461
final String name = 'format';
5562

5663
@override
5764
final String description =
58-
'Formats the code of all packages (Java, Objective-C, C++, and Dart).\n\n'
65+
'Formats the code of all packages (Java, Objective-C, C++, Dart, and '
66+
'optionally Swift).\n\n'
5967
'This command requires "git", "flutter" and "clang-format" v5 to be in '
6068
'your path.';
6169

@@ -71,6 +79,10 @@ class FormatCommand extends PackageCommand {
7179
await _formatDart(files);
7280
await _formatJava(files, googleFormatterPath);
7381
await _formatCppAndObjectiveC(files);
82+
final String? swiftFormat = getNullableStringArg(_swiftFormatArg);
83+
if (swiftFormat != null) {
84+
await _formatSwift(swiftFormat, files);
85+
}
7486

7587
if (getBoolArg('fail-on-change')) {
7688
final bool modified = await _didModifyAnything();
@@ -141,10 +153,24 @@ class FormatCommand extends PackageCommand {
141153
}
142154
}
143155

156+
Future<void> _formatSwift(String swiftFormat, Iterable<String> files) async {
157+
final Iterable<String> swiftFiles =
158+
_getPathsWithExtensions(files, <String>{'.swift'});
159+
if (swiftFiles.isNotEmpty) {
160+
print('Formatting .swift files...');
161+
final int exitCode =
162+
await _runBatched(swiftFormat, <String>['-i'], files: swiftFiles);
163+
if (exitCode != 0) {
164+
printError('Failed to format Swift files: exit code $exitCode.');
165+
throw ToolExit(_exitSwiftFormatFailed);
166+
}
167+
}
168+
}
169+
144170
Future<String> _findValidClangFormat() async {
145-
final String clangFormatArg = getStringArg('clang-format');
146-
if (await _hasDependency(clangFormatArg)) {
147-
return clangFormatArg;
171+
final String clangFormat = getStringArg(_clangFormatArg);
172+
if (await _hasDependency(clangFormat)) {
173+
return clangFormat;
148174
}
149175

150176
// There is a known issue where "chromium/depot_tools/clang-format"

script/tool/test/format_command_test.dart

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,71 @@ void main() {
428428
]));
429429
});
430430

431+
group('swift-format', () {
432+
test('formats Swift if --swift-format flag is provided', () async {
433+
const List<String> files = <String>[
434+
'macos/foo.swift',
435+
];
436+
final RepositoryPackage plugin = createFakePlugin(
437+
'a_plugin',
438+
packagesDir,
439+
extraFiles: files,
440+
);
441+
442+
await runCapturingPrint(
443+
runner, <String>['format', '--swift-format=/path/to/swift-format']);
444+
445+
expect(
446+
processRunner.recordedCalls,
447+
orderedEquals(<ProcessCall>[
448+
ProcessCall(
449+
'/path/to/swift-format',
450+
<String>['-i', ...getPackagesDirRelativePaths(plugin, files)],
451+
packagesDir.path),
452+
]));
453+
});
454+
455+
test('skips Swift if --swift-format flag is not provided', () async {
456+
const List<String> files = <String>[
457+
'macos/foo.swift',
458+
];
459+
createFakePlugin(
460+
'a_plugin',
461+
packagesDir,
462+
extraFiles: files,
463+
);
464+
465+
await runCapturingPrint(runner, <String>['format']);
466+
467+
expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[]));
468+
});
469+
470+
test('fails if swift-format fails', () async {
471+
const List<String> files = <String>[
472+
'macos/foo.swift',
473+
];
474+
createFakePlugin('a_plugin', packagesDir, extraFiles: files);
475+
476+
processRunner.mockProcessesForExecutable['swift-format'] =
477+
<FakeProcessInfo>[
478+
FakeProcessInfo(MockProcess(exitCode: 1), <String>['-i']),
479+
];
480+
Error? commandError;
481+
final List<String> output = await runCapturingPrint(
482+
runner, <String>['format', '--swift-format=swift-format'],
483+
errorHandler: (Error e) {
484+
commandError = e;
485+
});
486+
487+
expect(commandError, isA<ToolExit>());
488+
expect(
489+
output,
490+
containsAllInOrder(<Matcher>[
491+
contains('Failed to format Swift files: exit code 1.'),
492+
]));
493+
});
494+
});
495+
431496
test('skips known non-repo files', () async {
432497
const List<String> skipFiles = <String>[
433498
'/example/build/SomeFramework.framework/Headers/SomeFramework.h',

0 commit comments

Comments
 (0)