Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove the old CLI options and executables. #1565

Merged
merged 2 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
## 3.0.0-wip

* **Remove the old formatter executables and CLI options.**

Before the `dart format` command was added to the core Dart SDK, users
accessed the formatter by running a separate `dartfmt` executable that was
included with the Dart SDK. That executable had a different CLI interface.
For example, you had to pass `-w` to get it to overwrite files and if you
passed no arguments at all, it silently sat there waiting for input on stdin.
When we added `dart format`, we took that opportunity to revamp the CLI
options.

However, the dart_style package still exposed an executable with the old CLI.
If you ran `dart pub global activate dart_style`, this would give you a
`dartfmt` (and `dartformat`) executable with the old CLI options. Now that
almost everyone is using `dart format`, we have removed the old CLI and the
old package executables.

You can still run the formatter on the CLI through the package (for example,
if you want to use a particular version of dart_style instead of the one
bundled with your Dart SDK). But it now uses the exact same CLI options and
arguments as the `dart format` command. You can invoke it with
`dart run dart_style:format <args...>`.

## 2.3.7

* Allow passing a language version to `DartFomatter()`. Formatted code will be
Expand Down
174 changes: 13 additions & 161 deletions bin/format.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,169 +3,21 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:io';

import 'package:args/args.dart';
import 'package:dart_style/src/cli/formatter_options.dart';
import 'package:dart_style/src/cli/options.dart';
import 'package:dart_style/src/cli/output.dart';
import 'package:dart_style/src/cli/show.dart';
import 'package:dart_style/src/cli/summary.dart';
import 'package:dart_style/src/io.dart';
import 'package:dart_style/src/short/style_fix.dart';
import 'package:args/command_runner.dart';
import 'package:dart_style/src/cli/format_command.dart';

void main(List<String> args) async {
var parser = ArgParser(allowTrailingOptions: true);
void main(List<String> arguments) async {
var runner =
CommandRunner<int>('format', 'Idiomatically format Dart source code.');
runner.argParser.addFlag('verbose',
abbr: 'v', negatable: false, help: 'Show verbose help.');
runner.addCommand(FormatCommand(
verbose: arguments.contains('-v') || arguments.contains('--verbose')));

defineOptions(parser,
oldCli: true, verbose: args.contains('--verbose') || args.contains('-v'));

ArgResults argResults;
try {
argResults = parser.parse(args);
} on FormatException catch (err) {
usageError(parser, err.message);
}

if (argResults['help'] as bool) {
printUsage(parser);
return;
}

if (argResults['version'] as bool) {
print(dartStyleVersion);
return;
}

if (argResults['verbose'] as bool && !(argResults['help'] as bool)) {
usageError(parser, 'Can only use --verbose with --help.');
}

List<int>? selection;
try {
selection = parseSelection(argResults, 'preserve');
} on FormatException catch (exception) {
usageError(parser, exception.message);
}

if (argResults['dry-run'] as bool && argResults['overwrite'] as bool) {
usageError(
parser, 'Cannot use --dry-run and --overwrite at the same time.');
}

void checkForReporterCollision(String chosen, String other) {
if (!(argResults[other] as bool)) return;

usageError(parser, 'Cannot use --$chosen and --$other at the same time.');
}

var show = Show.legacy;
var summary = Summary.none;
var output = Output.show;
var setExitIfChanged = false;
if (argResults['dry-run'] as bool) {
checkForReporterCollision('dry-run', 'overwrite');
checkForReporterCollision('dry-run', 'machine');

show = Show.dryRun;
output = Output.none;
} else if (argResults['overwrite'] as bool) {
checkForReporterCollision('overwrite', 'machine');

if (argResults.rest.isEmpty) {
usageError(parser,
'Cannot use --overwrite without providing any paths to format.');
}

show = Show.overwrite;
output = Output.write;
} else if (argResults['machine'] as bool) {
output = Output.json;
}

if (argResults['profile'] as bool) summary = Summary.profile();

setExitIfChanged = argResults['set-exit-if-changed'] as bool;

int pageWidth;
try {
pageWidth = int.parse(argResults['line-length'] as String);
} on FormatException catch (_) {
usageError(
parser,
'--line-length must be an integer, was '
'"${argResults['line-length']}".');
}

int indent;
try {
indent = int.parse(argResults['indent'] as String);
if (indent < 0 || indent.toInt() != indent) throw const FormatException();
} on FormatException catch (_) {
usageError(
parser,
'--indent must be a non-negative integer, was '
'"${argResults['indent']}".');
}

var followLinks = argResults['follow-links'] as bool;

var fixes = <StyleFix>[];
if (argResults['fix'] as bool) fixes.addAll(StyleFix.all);
for (var fix in StyleFix.all) {
if (argResults['fix-${fix.name}'] as bool) {
if (argResults['fix'] as bool) {
usageError(parser, '--fix-${fix.name} is redundant with --fix.');
}

fixes.add(fix);
}
}

if (argResults.wasParsed('stdin-name') && argResults.rest.isNotEmpty) {
usageError(parser, 'Cannot pass --stdin-name when not reading from stdin.');
}

var options = FormatterOptions(
indent: indent,
pageWidth: pageWidth,
followLinks: followLinks,
fixes: fixes,
show: show,
output: output,
summary: summary,
setExitIfChanged: setExitIfChanged,
experimentFlags: argResults['enable-experiment'] as List<String>);

if (argResults.rest.isEmpty) {
await formatStdin(options, selection, argResults['stdin-name'] as String);
} else {
await formatPaths(options, argResults.rest);
}

options.summary.show();
}

/// Prints [error] and usage help then exits with exit code 64.
Never usageError(ArgParser parser, String error) {
printUsage(parser, error);
exit(64);
}

void printUsage(ArgParser parser, [String? error]) {
var output = stdout;

var message = 'Idiomatically format Dart source code.';
if (error != null) {
message = error;
output = stdout;
await runner.runCommand(runner.parse(['format', ...arguments]));
} on UsageException catch (exception) {
stderr.writeln(exception);
exit(64);
}

output.write('''$message

Usage: dartfmt [options...] [files or directories...]

Example: dartfmt -w .
Reformats every Dart file in the current directory tree.

${parser.usage}
''');
}
127 changes: 124 additions & 3 deletions lib/src/cli/format_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:io';

import 'package:args/args.dart';
import 'package:args/command_runner.dart';
import 'package:pub_semver/pub_semver.dart';

import '../dart_formatter.dart';
import '../io.dart';
import '../short/style_fix.dart';
import 'formatter_options.dart';
import 'options.dart';
import 'output.dart';
import 'show.dart';
import 'summary.dart';
Expand All @@ -27,7 +27,102 @@ class FormatCommand extends Command<int> {
'${runner!.executableName} $name [options...] <files or directories...>';

FormatCommand({bool verbose = false}) {
defineOptions(argParser, oldCli: false, verbose: verbose);
argParser.addFlag('verbose',
abbr: 'v',
negatable: false,
help: 'Show all options and flags with --help.');

if (verbose) argParser.addSeparator('Output options:');

argParser.addOption('output',
abbr: 'o',
help: 'Set where to write formatted output.',
allowed: ['write', 'show', 'json', 'none'],
allowedHelp: {
'write': 'Overwrite formatted files on disk.',
'show': 'Print code to terminal.',
'json': 'Print code and selection as JSON.',
'none': 'Discard output.'
},
defaultsTo: 'write');
argParser.addOption('show',
help: 'Set which filenames to print.',
allowed: ['all', 'changed', 'none'],
allowedHelp: {
'all': 'All visited files and directories.',
'changed': 'Only the names of files whose formatting is changed.',
'none': 'No file names or directories.',
},
defaultsTo: 'changed',
hide: !verbose);
argParser.addOption('summary',
help: 'Show the specified summary after formatting.',
allowed: ['line', 'profile', 'none'],
allowedHelp: {
'line': 'Single-line summary.',
'profile': 'How long it took for format each file.',
'none': 'No summary.'
},
defaultsTo: 'line',
hide: !verbose);

argParser.addOption('language-version',
help: 'Language version of formatted code.\n'
'Use "latest" to parse as the latest supported version.\n'
'Omit to look for a surrounding package config.',
// TODO(rnystrom): Show this when the tall-style experiment ships.
hide: true);

argParser.addFlag('set-exit-if-changed',
negatable: false,
help: 'Return exit code 1 if there are any formatting changes.');

if (verbose) {
argParser.addSeparator('Non-whitespace fixes (off by default):');
}

argParser.addFlag('fix',
negatable: false, help: 'Apply all style fixes.', hide: !verbose);

for (var fix in StyleFix.all) {
argParser.addFlag('fix-${fix.name}',
negatable: false, help: fix.description, hide: !verbose);
}

if (verbose) argParser.addSeparator('Other options:');

argParser.addOption('line-length',
abbr: 'l',
help: 'Wrap lines longer than this.',
defaultsTo: '80',
hide: true);
argParser.addOption('indent',
abbr: 'i',
help: 'Add this many spaces of leading indentation.',
defaultsTo: '0',
hide: !verbose);

argParser.addFlag('follow-links',
negatable: false,
help: 'Follow links to files and directories.\n'
'If unset, links will be ignored.',
hide: !verbose);
argParser.addFlag('version',
negatable: false, help: 'Show dart_style version.', hide: !verbose);
argParser.addMultiOption('enable-experiment',
help: 'Enable one or more experimental features.\n'
'See dart.dev/go/experiments.',
hide: !verbose);

if (verbose) argParser.addSeparator('Options when formatting from stdin:');

argParser.addOption('selection',
help: 'Track selection (given as "start:length") through formatting.',
hide: !verbose);
argParser.addOption('stdin-name',
help: 'Use this path in error messages when input is read from stdin.',
defaultsTo: 'stdin',
hide: !verbose);
}

@override
Expand Down Expand Up @@ -130,7 +225,7 @@ class FormatCommand extends Command<int> {

List<int>? selection;
try {
selection = parseSelection(argResults, 'selection');
selection = _parseSelection(argResults, 'selection');
} on FormatException catch (exception) {
usageException(exception.message);
}
Expand Down Expand Up @@ -178,4 +273,30 @@ class FormatCommand extends Command<int> {
// and set their own exitCode.
return exitCode;
}

List<int>? _parseSelection(ArgResults argResults, String optionName) {
var option = argResults[optionName] as String?;
if (option == null) return null;

// Can only preserve a selection when parsing from stdin.
if (argResults.rest.isNotEmpty) {
throw FormatException(
'Can only use --$optionName when reading from stdin.');
}

try {
var coordinates = option.split(':');
if (coordinates.length != 2) {
throw const FormatException(
'Selection should be a colon-separated pair of integers, '
'"123:45".');
}

return coordinates.map<int>((coord) => int.parse(coord.trim())).toList();
} on FormatException catch (_) {
throw FormatException(
'--$optionName must be a colon-separated pair of integers, was '
'"${argResults[optionName]}".');
}
}
}
17 changes: 0 additions & 17 deletions lib/src/cli/formatter_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -95,21 +95,4 @@ class FormatterOptions {
// Set the exit code.
if (setExitIfChanged && changed) exitCode = 1;
}

/// Describes the directory whose contents are about to be processed.
void showDirectory(String path) {
if (output != Output.json) {
show.directory(path);
}
}

/// Describes the symlink at [path] that wasn't followed.
void showSkippedLink(String path) {
show.skippedLink(path);
}

/// Describes the hidden [path] that wasn't processed.
void showHiddenPath(String path) {
show.hiddenPath(path);
}
}
Loading