blob: f06182546bf3bc4d5f6cb674f53ed80366e0f639 [file] [log] [blame] [edit]
// Copyright (c) 2014, 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:convert';
import 'dart:io';
import 'package:path/path.dart' as p;
import 'cli/formatter_options.dart';
import 'config_cache.dart';
import 'dart_formatter.dart';
import 'exceptions.dart';
import 'source_code.dart';
/// Reads and formats input from stdin until closed.
Future<void> formatStdin(
FormatterOptions options,
List<int>? selection,
String? path,
) async {
var selectionStart = 0;
var selectionLength = 0;
if (selection != null) {
selectionStart = selection[0];
selectionLength = selection[1];
}
var cache = ConfigCache();
var languageVersion = options.languageVersion;
if (languageVersion == null && path != null) {
// We have a stdin-name, so look for a surrounding package config.
languageVersion = await cache.findLanguageVersion(File(path), path);
}
// If they didn't specify a version or a path, or couldn't find a package
// surrounding the path, then default to the latest version.
languageVersion ??= DartFormatter.latestLanguageVersion;
// Determine the page width.
var pageWidth = options.pageWidth;
if (pageWidth == null && path != null) {
// We have a stdin-name, so look for a surrounding analyisis_options.yaml.
pageWidth = await cache.findPageWidth(File(path));
}
var trailingCommas = options.trailingCommas;
if (trailingCommas == null && path != null) {
// We have a stdin-name, so look for a surrounding analyisis_options.yaml.
trailingCommas = await cache.findTrailingCommas(File(path));
}
// Use a default page width if we don't have a specified one and couldn't
// find a configured one.
pageWidth ??= DartFormatter.defaultPageWidth;
var name = path ?? 'stdin';
var completer = Completer<void>();
var input = StringBuffer();
void onDone() {
var formatter = DartFormatter(
languageVersion: languageVersion!,
indent: options.indent,
pageWidth: pageWidth,
trailingCommas: trailingCommas,
experimentFlags: options.experimentFlags,
);
try {
options.beforeFile(null, name);
var source = SourceCode(
input.toString(),
uri: path,
selectionStart: selectionStart,
selectionLength: selectionLength,
);
var output = formatter.formatSource(source);
options.afterFile(
null,
name,
source,
output,
changed: source.text != output.text,
);
} on FormatterException catch (err) {
stderr.writeln(err.message());
exitCode = 65; // sysexits.h: EX_DATAERR
} catch (err, stack) {
stderr.writeln('''Hit a bug in the formatter when formatting stdin.
Please report at: github.com/dart-lang/dart_style/issues
$err
$stack''');
exitCode = 70; // sysexits.h: EX_SOFTWARE
}
completer.complete();
}
stdin.transform(const Utf8Decoder()).listen(input.write, onDone: onDone);
return completer.future;
}
/// Formats all of the files and directories given by [paths].
Future<void> formatPaths(FormatterOptions options, List<String> paths) async {
// If the user didn't specify a language version, then look for surrounding
// package configs so we know what language versions to use for the files.
var cache = ConfigCache();
for (var path in paths) {
var directory = Directory(path);
if (directory.existsSync()) {
if (!await _processDirectory(cache, options, directory)) {
exitCode = 65;
}
continue;
}
var file = File(path);
if (file.existsSync()) {
if (!await _processFile(cache, options, file)) {
exitCode = 65;
}
} else {
stderr.writeln('No file or directory found at "$path".');
}
}
}
/// Runs the formatter on every .dart file in [path] (and its subdirectories),
/// and replaces them with their formatted output.
///
/// Returns `true` if successful or `false` if an error occurred in any of the
/// files.
Future<bool> _processDirectory(
ConfigCache cache,
FormatterOptions options,
Directory directory,
) async {
var success = true;
var entries = directory.listSync(
recursive: true,
followLinks: options.followLinks,
);
entries.sort((a, b) => a.path.compareTo(b.path));
for (var entry in entries) {
if (entry is Link) 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));
if (parts.any((part) => part.startsWith('.'))) continue;
if (!await _processFile(
cache,
options,
entry,
displayPath: p.normalize(entry.path),
)) {
success = false;
}
}
return success;
}
/// Runs the formatter on [file].
///
/// Returns `true` if successful or `false` if an error occurred.
Future<bool> _processFile(
ConfigCache cache,
FormatterOptions options,
File file, {
String? displayPath,
}) async {
displayPath ??= file.path;
// Determine what language version to use.
var languageVersion =
options.languageVersion ??
await cache.findLanguageVersion(file, displayPath);
// If they didn't specify a version and we couldn't find a surrounding
// package, then default to the latest version.
languageVersion ??= DartFormatter.latestLanguageVersion;
// Determine the configuration options.
var pageWidth = options.pageWidth ?? await cache.findPageWidth(file);
var trailingCommas =
options.trailingCommas ?? await cache.findTrailingCommas(file);
// Use a default page width if we don't have a specified one and couldn't
// find a configured one.
pageWidth ??= DartFormatter.defaultPageWidth;
var formatter = DartFormatter(
languageVersion: languageVersion,
indent: options.indent,
pageWidth: pageWidth,
trailingCommas: trailingCommas,
experimentFlags: options.experimentFlags,
);
try {
var source = SourceCode(file.readAsStringSync(), uri: file.path);
options.beforeFile(file, displayPath);
var output = formatter.formatSource(source);
options.afterFile(
file,
displayPath,
source,
output,
changed: source.text != output.text,
);
return true;
} 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''');
}
return false;
}