// 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 formatStdin( FormatterOptions options, List? 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(); 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, 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 formatPaths(FormatterOptions options, List 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 _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 _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, 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; }