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

Add import sorting to format tool (backpatch to v3.7.1) #381

Merged
merged 25 commits into from
Jul 25, 2022
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e1e1057
Refactor: Copy over import_cleaner tool
regenvanwalbeek-wf Jul 6, 2022
677af58
Refactor: Let buildExecution return multiple processes
regenvanwalbeek-wf Jul 11, 2022
dbe74bc
Remove unused imports
regenvanwalbeek-wf Jul 11, 2022
e9b060f
Refactor: `buildProcess` -> `buildFormatProcess`
regenvanwalbeek-wf Jul 12, 2022
24d0ec3
Allow formatter to call import sorter
regenvanwalbeek-wf Jul 14, 2022
983bb53
Do not sort own package imports differently from other package imports
regenvanwalbeek-wf Jul 14, 2022
91d86d7
Fix dependency_validator
regenvanwalbeek-wf Jul 14, 2022
6911398
Backpatch fix: Pin meta down
regenvanwalbeek-wf Jul 14, 2022
e849fb7
Remove stable, dev from backpatch release line
regenvanwalbeek-wf Jul 14, 2022
62232eb
Remove unused directoriesToInclude
regenvanwalbeek-wf Jul 14, 2022
72122f9
Bugfix: Don't replace quotes in comments
regenvanwalbeek-wf Jul 14, 2022
85bd79f
Organize imports in this repo
regenvanwalbeek-wf Jul 14, 2022
90b488f
organizeImports as part of format tool
evanweible-wf Jul 19, 2022
1a8aba9
Consistent nomenclature, styling, formatting
regenvanwalbeek-wf Jul 20, 2022
c95cd9f
Backpatch fix: disable pub publish check
regenvanwalbeek-wf Jul 20, 2022
5927baa
Backpatch fix: Pin meta so others dont update too far
regenvanwalbeek-wf Jul 20, 2022
8029ed7
Update FormatExecution doc strings
regenvanwalbeek-wf Jul 20, 2022
7133387
Organize imports for all valid files
regenvanwalbeek-wf Jul 20, 2022
757aa0c
organize exports and imports
regenvanwalbeek-wf Jul 21, 2022
58be5f2
Refactor: Break down `organizeImports`
regenvanwalbeek-wf Jul 21, 2022
0a9001e
Refactor: _assignCommentsInFileToNamespaceDirective creates Namespace
regenvanwalbeek-wf Jul 21, 2022
37fe8de
Add functional test for formatter
regenvanwalbeek-wf Jul 21, 2022
17d6e60
Renaming: import->directive
regenvanwalbeek-wf Jul 21, 2022
f349016
Remove unnecessary lock, failure msg
regenvanwalbeek-wf Jul 21, 2022
68fab5a
Add changelog entry.
regenvanwalbeek-wf Jul 25, 2022
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
4 changes: 2 additions & 2 deletions .github/workflows/dart_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
strategy:
fail-fast: false
matrix:
sdk: [ 2.7.2, 2.13.4, stable, dev ]
sdk: [ 2.7.2, 2.13.4 ]
regenvanwalbeek-wf marked this conversation as resolved.
Show resolved Hide resolved
steps:
- uses: actions/checkout@v2
- uses: dart-lang/[email protected]
Expand All @@ -30,7 +30,7 @@ jobs:
run: pub get

- name: Validate dependencies
run: pub run dependency_validator -i pedantic,over_react_format,meta
run: pub run dependency_validator
if: always() && steps.install.outcome == 'success'

- name: Analyze project source
Expand Down
15 changes: 15 additions & 0 deletions doc/tools/format-tool.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,21 @@ final config = {
};
```

### Organizing imports

By default, the format tool will not sort imports. Imports can be automatically
sorted by setting `organizeImports`.

```dart
// tool/dart_dev/config.dart
import 'package:dart_dev/dart_dev.dart';

final config = {
'format': FormatTool()
..organizeImports = true
};
```

## Command-line options

```bash
Expand Down
2 changes: 0 additions & 2 deletions lib/src/dart_dev_runner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import 'dart_dev_tool.dart';
import 'events.dart' as events;
import 'utils/version.dart';

// import 'package:completion/completion.dart' as completion;

class DartDevRunner extends CommandRunner<int> {
DartDevRunner(Map<String, DevTool> commands)
: super('dart_dev', 'Dart tool runner.') {
Expand Down
23 changes: 22 additions & 1 deletion lib/src/executable.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import 'dart:async';
import 'dart:io';
import 'dart:math';

import 'package:analyzer/dart/analysis/utilities.dart';
import 'package:args/command_runner.dart';
import 'package:dart_dev/dart_dev.dart';
import 'package:dart_dev/src/dart_dev_tool.dart';
import 'package:dart_dev/src/tools/import_cleaner_tool.dart';
import 'package:dart_dev/src/utils/format_tool_builder.dart';
import 'package:dart_dev/src/utils/parse_flag_from_args.dart';
import 'package:io/ansi.dart';
Expand Down Expand Up @@ -58,6 +58,11 @@ Future<void> run(List<String> args) async {
return;
}

if (args.contains('sort_imports')) {
await handleSortImports(args);
return;
}
regenvanwalbeek-wf marked this conversation as resolved.
Show resolved Hide resolved

generateRunScript();
final process = await Process.start(
Platform.executable, [_runScriptPath, ...args],
Expand Down Expand Up @@ -102,6 +107,22 @@ Future<void> handleFastFormat(List<String> args) async {
}
}

Future<void> handleSortImports(List<String> args) async {
assertDirIsDartPackage();

try {
exitCode =
await DartDevRunner({'sort_imports': ImportCleanerTool()}).run(args);
} catch (error, stack) {
log.severe('Uncaught Exception:', error, stack);
if (!parseFlagFromArgs(args, 'verbose', abbr: 'v')) {
// Always print the stack trace for an uncaught exception.
stderr.writeln(stack);
}
exitCode = ExitCode.unavailable.code;
}
}

void generateRunScript() {
if (shouldWriteRunScript) {
logTimedSync(log, 'Generating run script', () {
Expand Down
82 changes: 68 additions & 14 deletions lib/src/tools/format_tool.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ class FormatTool extends DevTool {
/// Run `dartfmt -h -v` to see all available args.
List<String> formatterArgs;

/// If the formatter should also organize imports.
///
/// By default, this is disabled.
bool organizeImports = false;
regenvanwalbeek-wf marked this conversation as resolved.
Show resolved Hide resolved

// ---------------------------------------------------------------------------
// DevTool Overrides
// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -97,17 +102,27 @@ class FormatTool extends DevTool {
String description = 'Format dart files in this package.';

@override
FutureOr<int> run([DevToolExecutionContext context]) {
FutureOr<int> run([DevToolExecutionContext context]) async {
context ??= DevToolExecutionContext();
final execution = buildExecution(
context,
configuredFormatterArgs: formatterArgs,
defaultMode: defaultMode,
exclude: exclude,
formatter: formatter,
organizeImports: organizeImports,
);
return execution.exitCode ??
runProcessAndEnsureExit(execution.process, log: _log);
if (execution.exitCode != null) {
return execution.exitCode;
}

for (final process in execution.processes) {
final exitCode = await runProcessAndEnsureExit(process, log: _log);
if (exitCode != 0) {
return exitCode;
}
}
return 0;
}

/// Builds and returns the object that contains:
Expand Down Expand Up @@ -283,25 +298,26 @@ class FormatterInputs {
/// A declarative representation of an execution of the [FormatTool].
///
/// This class allows the [FormatTool] to break its execution up into two steps:
/// 1. Validation of confg/inputs and creation of this class.
/// 1. Validation of config/inputs and creation of this class.
/// 2. Execution of expensive or hard-to-test logic based on step 1.
///
/// As a result, nearly all of the logic in [FormatTool] can be tested via the
/// output of step 1 (an instance of this class) with very simple unit tests.
class FormatExecution {
FormatExecution.exitEarly(this.exitCode) : process = null;
FormatExecution.process(this.process) : exitCode = null;
FormatExecution.exitEarly(this.exitCode) : processes = null;
FormatExecution.process(this.processes) : exitCode = null;

/// If non-null, the execution is already complete and the [FormatTool] should
/// exit with this code.
///
/// If null, there is more work to do.
final int exitCode;

/// A declarative representation of the formatter process that should be run.
/// A declarative representation of the formatter processes that should be run.
regenvanwalbeek-wf marked this conversation as resolved.
Show resolved Hide resolved
///
/// This process' result should become the final result of the [FormatTool].
final ProcessDeclaration process;
/// If all processes result in exit code 0, [FormatTool] will return with exit code 0.
/// Otherwise, the first non-zero exit code will become the final result of the [FormatTool].
regenvanwalbeek-wf marked this conversation as resolved.
Show resolved Hide resolved
final Iterable<ProcessDeclaration> processes;
}

/// Modes supported by the dart formatter.
Expand Down Expand Up @@ -385,6 +401,8 @@ Iterable<String> buildArgs(
///
/// [include] will be populated from [FormatTool.include].
///
/// [organizeImports] will be populated from [FormatTool.organizeImports].
///
/// If non-null, [path] will override the current working directory for any
/// operations that require it. This is intended for use by tests.
///
Expand All @@ -397,6 +415,7 @@ FormatExecution buildExecution(
FormatMode defaultMode,
List<Glob> exclude,
Formatter formatter,
bool organizeImports = false,
String path,
}) {
FormatMode mode;
Expand Down Expand Up @@ -463,23 +482,35 @@ FormatExecution buildExecution(
'${inputs.hiddenDirectories.join('\n ')}');
}

final dartfmt = buildProcess(formatter);
final sortImportProcess = buildSortImportProcess(
inputs.includedFiles,
mode,
organizeImports: organizeImports,
);
final dartfmt = buildFormatProcess(formatter);
final args = buildArgs(dartfmt.args, mode,
argResults: context.argResults,
configuredFormatterArgs: configuredFormatterArgs);
logCommand(dartfmt.executable, inputs.includedFiles, args,
verbose: context.verbose);
return FormatExecution.process(ProcessDeclaration(
dartfmt.executable, [...args, ...inputs.includedFiles],
mode: ProcessStartMode.inheritStdio));

final formatProcess = ProcessDeclaration(
dartfmt.executable,
[...args, ...inputs.includedFiles],
mode: ProcessStartMode.inheritStdio,
);
return FormatExecution.process([
if (sortImportProcess != null) sortImportProcess,
formatProcess,
]);
}

/// Returns a representation of the process that will be run by [FormatTool]
/// based on the given [formatter].
///
/// - [Formatter.dartfmt] -> `dartfmt`
/// - [Formatter.dartStyle] -> `pub run dart_style:format`
ProcessDeclaration buildProcess([Formatter formatter]) {
ProcessDeclaration buildFormatProcess([Formatter formatter]) {
switch (formatter) {
case Formatter.dartStyle:
return ProcessDeclaration('pub', ['run', 'dart_style:format']);
Expand All @@ -489,6 +520,29 @@ ProcessDeclaration buildProcess([Formatter formatter]) {
}
}

ProcessDeclaration buildSortImportProcess(
Iterable<String> includedFiles,
FormatMode mode, {
bool organizeImports = false,
}) {
if (!organizeImports) {
return null;
}

return ProcessDeclaration(
'dart',
[
'run',
'dart_dev',
'sort_imports',
if (mode == FormatMode.check) '--check',
'--files',
includedFiles.join(','),
],
mode: ProcessStartMode.inheritStdio,
);
}

/// Logs the dart formatter command that will be run by [FormatTool] so that
/// consumers can run it directly for debugging purposes.
///
Expand Down
1 change: 0 additions & 1 deletion lib/src/tools/function_tool.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'dart:async';

import 'package:args/args.dart';
import 'package:dart_dev/src/utils/assert_no_args_after_separator.dart';
import 'package:io/io.dart';
import 'package:logging/logging.dart';

Expand Down
Loading