[analyzer] use preferred quote-style when generating imports

Bug #49559

Change-Id: Ib77ea67bb1ea15bfabb1c717cfb5abf13fd6d3cb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/259720
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Ahmed Ashour 2022-09-21 17:12:46 +00:00 committed by Commit Bot
parent f9355b1bf2
commit e09493b7bc
6 changed files with 125 additions and 2 deletions

View file

@ -2,6 +2,8 @@
// 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 'package:analyzer/dart/ast/ast.dart';
/// A set of options related to coding style that apply to the code within a
/// single analysis context.
///
@ -14,6 +16,10 @@ abstract class CodeStyleOptions {
/// Return `true` if local variables should be `final` whenever possible.
bool get makeLocalsFinal;
/// Return the preferred quote based on the enabled lints,otherwise a single
/// quote.
String get preferredQuoteForStrings;
/// Return `true` if constructors should be sorted first, before other
/// class members.
bool get sortConstructorsFirst;
@ -25,4 +31,8 @@ abstract class CodeStyleOptions {
/// Return `true` if URIs should be "relative", meaning without a scheme,
/// whenever possible.
bool get useRelativeUris;
/// Return the preferred quote based on the enabled lints, otherwise based
/// on the first directive, otherwise a single quote.
String preferredQuoteForUris(List<ImportDirective> directives);
}

View file

@ -4,6 +4,8 @@
import 'package:analyzer/dart/analysis/analysis_options.dart';
import 'package:analyzer/dart/analysis/code_style_options.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:collection/collection.dart';
/// The concrete implementation of [CodeStyleOptions].
class CodeStyleOptionsImpl implements CodeStyleOptions {
@ -22,12 +24,34 @@ class CodeStyleOptionsImpl implements CodeStyleOptions {
@override
bool get makeLocalsFinal => _isLintEnabled('prefer_final_locals');
@override
String get preferredQuoteForStrings => _lintQuote() ?? "'";
@override
bool get sortConstructorsFirst => _isLintEnabled('sort_constructors_first');
@override
bool get useRelativeUris => _isLintEnabled('prefer_relative_imports');
@override
String preferredQuoteForUris(List<ImportDirective> directives) {
var lintQuote = _lintQuote();
if (lintQuote != null) {
return lintQuote;
}
var uri = directives.firstOrNull?.uri;
var doubleQuote = uri is SimpleStringLiteral &&
uri.literal.value().toString().startsWith('"');
return doubleQuote ? '"' : "'";
}
/// Return `true` if the lint with the given [name] is enabled.
bool _isLintEnabled(String name) => options.isLintEnabled(name);
/// Return the preferred lint quote, otherwise `null`.
String? _lintQuote() => _isLintEnabled("prefer_single_quotes")
? "'"
: _isLintEnabled("prefer_double_quotes")
? '"'
: null;
}

View file

@ -1600,10 +1600,11 @@ class DartFileEditBuilderImpl extends FileEditBuilderImpl
var importList = imports.toList();
importList.sort((a, b) => a.uriText.compareTo(b.uriText));
var quote = codeStyleOptions.preferredQuoteForUris(importDirectives);
void writeImport(EditBuilder builder, _LibraryToImport import) {
builder.write("import '");
builder.write('import $quote');
builder.write(import.uriText);
builder.write("'");
builder.write(quote);
var prefix = import.prefix;
if (prefix != null) {
builder.write(' as ');

View file

@ -20,6 +20,7 @@ dependencies:
dev_dependencies:
analyzer_utilities: any
lints: any
linter: any
meta: any
path: any
test_reflective_loader: any

View file

@ -15,6 +15,7 @@ import 'package:analyzer/src/test_utilities/package_config_file_builder.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:analyzer_plugin/src/utilities/change_builder/change_builder_dart.dart'
show DartLinkedEditBuilderImpl;
import 'package:linter/src/rules.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@ -2088,6 +2089,43 @@ import 'package:foo/foo.dart';
);
}
Future<void> test_default_quote() async {
await _assertImportLibrary(
initialCode: '''
''',
uriList: ['dart:aaa'],
expectedCode: '''
import 'dart:aaa';
''',
);
}
Future<void> test_directive_double_quote() async {
await _assertImportLibrary(
initialCode: '''
import "dart:bbb";
''',
uriList: ['dart:aaa'],
expectedCode: '''
import "dart:aaa";
import "dart:bbb";
''',
);
}
Future<void> test_directive_single_quote() async {
await _assertImportLibrary(
initialCode: '''
import 'dart:bbb';
''',
uriList: ['dart:aaa'],
expectedCode: '''
import 'dart:aaa';
import 'dart:bbb';
''',
);
}
Future<void> test_multiple_dart_then_package() async {
await _assertImportLibrary(
initialCode: '''
@ -2395,6 +2433,36 @@ import 'foo.dart';
);
}
Future<void> test_prefer_double_quotes() async {
registerLintRules();
writeTestPackageAnalysisOptionsFile(lints: ['prefer_double_quotes']);
await _assertImportLibrary(
initialCode: '''
import 'dart:bbb';
''',
uriList: ['dart:aaa'],
expectedCode: '''
import "dart:aaa";
import 'dart:bbb';
''',
);
}
Future<void> test_prefer_single_quotes() async {
registerLintRules();
writeTestPackageAnalysisOptionsFile(lints: ['prefer_single_quotes']);
await _assertImportLibrary(
initialCode: '''
import "dart:bbb";
''',
uriList: ['dart:aaa'],
expectedCode: '''
import 'dart:aaa';
import "dart:bbb";
''',
);
}
Future<void> test_relative_afterDart() async {
await _assertImportLibrary(
initialCode: '''

View file

@ -126,6 +126,25 @@ class AbstractContextTest with ResourceProviderMixin {
newFile(path, config.toContent(toUriStr: toUriStr));
}
/// Write an analysis options file based on the given arguments.
/// TODO(asashour) Use AnalysisOptionsFileConfig
void writeTestPackageAnalysisOptionsFile({
List<String>? lints,
}) {
var buffer = StringBuffer();
if (lints != null) {
buffer.writeln('linter:');
buffer.writeln(' rules:');
for (var lint in lints) {
buffer.writeln(' - $lint');
}
}
print(buffer);
newFile('$testPackageRootPath/analysis_options.yaml', buffer.toString());
}
void writeTestPackageConfig({
PackageConfigFileBuilder? config,
String? languageVersion,