[analyzer] Add support for "formatter" in analysis_options + use page_width in LSP
This adds support for validation + completion for `formatter/page_width` in analysis_options, and uses this value in preference to the client-supplied value in the LSP server.
It does not yet add support for the legacy protocol, and there are a few questions in https://github.com/dart-lang/sdk/issues/56864#issuecomment-2399289974.
See https://github.com/dart-lang/sdk/issues/56864
Change-Id: I7844e3abd1191beb9cc24197bbc8aa7c9e9ad4fa
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/388822
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analysis_server/lib/src/lsp/client_configuration.dart b/pkg/analysis_server/lib/src/lsp/client_configuration.dart
index 1482139..17545ea 100644
--- a/pkg/analysis_server/lib/src/lsp/client_configuration.dart
+++ b/pkg/analysis_server/lib/src/lsp/client_configuration.dart
@@ -286,7 +286,8 @@
true;
}
- /// The line length used when formatting documents.
+ /// The line length used when formatting documents if not specified in
+ /// `analysis_options.yaml`.
///
/// If null, the formatters default will be used.
int? get lineLength =>
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_format_on_type.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_format_on_type.dart
index 53bb3f3..7def579 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_format_on_type.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_format_on_type.dart
@@ -39,7 +39,7 @@
}
var lineLength = server.lspClientConfiguration.forResource(path).lineLength;
- return generateEditsForFormatting(result, lineLength);
+ return generateEditsForFormatting(result, defaultPageWidth: lineLength);
}
@override
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_format_range.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_format_range.dart
index 48297a9..eb39038 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_format_range.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_format_range.dart
@@ -39,7 +39,8 @@
}
var lineLength = server.lspClientConfiguration.forResource(path).lineLength;
- return generateEditsForFormatting(result, lineLength, range: range);
+ return generateEditsForFormatting(result,
+ defaultPageWidth: lineLength, range: range);
}
@override
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_formatting.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_formatting.dart
index 22e6492..411bb66 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_formatting.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_formatting.dart
@@ -39,7 +39,7 @@
}
var lineLength = server.lspClientConfiguration.forResource(path).lineLength;
- return generateEditsForFormatting(result, lineLength);
+ return generateEditsForFormatting(result, defaultPageWidth: lineLength);
}
@override
diff --git a/pkg/analysis_server/lib/src/lsp/source_edits.dart b/pkg/analysis_server/lib/src/lsp/source_edits.dart
index 7055cce..91fcdd0 100644
--- a/pkg/analysis_server/lib/src/lsp/source_edits.dart
+++ b/pkg/analysis_server/lib/src/lsp/source_edits.dart
@@ -78,13 +78,25 @@
return ErrorOr.success((content: newContent, edits: serverEdits));
}
+/// Generates a list of [TextEdit]s to format the code for [result].
+///
+/// [defaultPageWidth] will be used as the default page width if [result] does
+/// not have an analysis_options file that defines a page width.
+///
+/// If [range] is provided, only edits that intersect with this range will be
+/// returned.
ErrorOr<List<TextEdit>?> generateEditsForFormatting(
- ParsedUnitResult result,
- int? lineLength, {
+ ParsedUnitResult result, {
+ int? defaultPageWidth,
Range? range,
}) {
var unformattedSource = result.content;
+ // The analysis options page width always takes priority over the default from
+ // the LSP configuration.
+ var effectivePageWidth =
+ result.analysisOptions.formatterOptions.pageWidth ?? defaultPageWidth;
+
var code = SourceCode(unformattedSource);
SourceCode formattedResult;
try {
@@ -94,9 +106,9 @@
var languageVersion =
result.unit.declaredElement?.library.languageVersion.effective ??
DartFormatter.latestLanguageVersion;
- formattedResult =
- DartFormatter(pageWidth: lineLength, languageVersion: languageVersion)
- .formatSource(code);
+ var formatter = DartFormatter(
+ pageWidth: effectivePageWidth, languageVersion: languageVersion);
+ formattedResult = formatter.formatSource(code);
} on FormatterException {
// If the document fails to parse, just return no edits to avoid the
// use seeing edits on every save with invalid code (if LSP gains the
diff --git a/pkg/analysis_server/lib/src/services/completion/yaml/analysis_options_generator.dart b/pkg/analysis_server/lib/src/services/completion/yaml/analysis_options_generator.dart
index 62d845a..679f98f 100644
--- a/pkg/analysis_server/lib/src/services/completion/yaml/analysis_options_generator.dart
+++ b/pkg/analysis_server/lib/src/services/completion/yaml/analysis_options_generator.dart
@@ -39,6 +39,9 @@
AnalyzerOptions.codeStyle: MapProducer({
AnalyzerOptions.format: BooleanProducer(),
}),
+ AnalyzerOptions.formatter: MapProducer({
+ AnalyzerOptions.pageWidth: EmptyProducer(),
+ }),
// TODO(brianwilkerson): Create a producer to produce `package:` URIs.
AnalyzerOptions.include: EmptyProducer(),
// TODO(brianwilkerson): Create constants for 'linter' and 'rules'.
diff --git a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
index 2bd3783..2785d97 100644
--- a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
+++ b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
@@ -829,7 +829,7 @@
continue;
}
- var formatResult = generateEditsForFormatting(result, null);
+ var formatResult = generateEditsForFormatting(result);
await formatResult.mapResult((formatResult) async {
var edits = formatResult ?? [];
if (edits.isNotEmpty) {
diff --git a/pkg/analysis_server/test/domain_completion_test.dart b/pkg/analysis_server/test/domain_completion_test.dart
index 02d0c24..0f1075b 100644
--- a/pkg/analysis_server/test/domain_completion_test.dart
+++ b/pkg/analysis_server/test/domain_completion_test.dart
@@ -2035,6 +2035,8 @@
kind: identifier
|code-style: |
kind: identifier
+ |formatter: |
+ kind: identifier
|include: |
kind: identifier
|linter: |
diff --git a/pkg/analysis_server/test/lsp/format_test.dart b/pkg/analysis_server/test/lsp/format_test.dart
index 823e5e0..d51df2e 100644
--- a/pkg/analysis_server/test/lsp/format_test.dart
+++ b/pkg/analysis_server/test/lsp/format_test.dart
@@ -20,6 +20,11 @@
@reflectiveTest
class FormatTest extends AbstractLspAnalysisServerTest {
+ /// Some sample code that is over 50 characters and will be wrapped if the
+ /// page width is set to 40.
+ static const codeThatWrapsAt40 =
+ "var a = ' 10 20 30 40';";
+
Future<List<TextEdit>> expectFormattedContents(
Uri uri, String original, String expected) async {
var formatEdits = (await formatDocument(uri))!;
@@ -36,6 +41,11 @@
return formatEdits;
}
+ Future<String> formatContents(Uri uri, String original) async {
+ var formatEdits = (await formatDocument(uri))!;
+ return applyTextEdits(original, formatEdits);
+ }
+
Future<void> test_alreadyFormatted() async {
const contents = '''
void f() {
@@ -383,6 +393,83 @@
await expectFormattedContents(mainFileUri, contents, expectedLongLines);
}
+ Future<void> test_lineLength_analysisOptions() async {
+ const codeContent = codeThatWrapsAt40;
+ const optionsContent = '''
+formatter:
+ page_width: 40
+''';
+
+ newFile(analysisOptionsPath, optionsContent);
+ newFile(mainFilePath, codeContent);
+ await initialize();
+
+ // Ignore trailing newlines when checking for wrapping.
+ var formatted = await formatContents(mainFileUri, codeContent);
+ expect(formatted.trim(), contains('\n'));
+ }
+
+ Future<void> test_lineLength_analysisOptions_nestedEmpty() async {
+ const codeContent = codeThatWrapsAt40;
+ const optionsContent = '''
+formatter:
+ page_width: 40
+''';
+ var nestedAnalysisOptionsPath =
+ join(projectFolderPath, 'lib', 'analysis_options.yaml');
+
+ newFile(analysisOptionsPath, optionsContent);
+ newFile(
+ nestedAnalysisOptionsPath, '# empty'); // suppress the parent options.
+ newFile(mainFilePath, codeContent);
+ await initialize();
+
+ // Ignore trailing newlines when checking for wrapping.
+ var formatted = await formatContents(mainFileUri, codeContent);
+ expect(formatted.trim(), isNot(contains('\n')));
+ }
+
+ Future<void> test_lineLength_analysisOptions_nestedIncludes() async {
+ const codeContent = codeThatWrapsAt40;
+ const optionsContent = '''
+formatter:
+ page_width: 40
+''';
+ var nestedAnalysisOptionsPath =
+ join(projectFolderPath, 'lib', 'analysis_options.yaml');
+
+ newFile(analysisOptionsPath, optionsContent);
+ newFile(nestedAnalysisOptionsPath, 'include: ../analysis_options.yaml');
+ newFile(mainFilePath, codeContent);
+ await initialize();
+
+ // Ignore trailing newlines when checking for wrapping.
+ var formatted = await formatContents(mainFileUri, codeContent);
+ expect(formatted.trim(), contains('\n'));
+ }
+
+ Future<void> test_lineLength_analysisOptions_overridesConfig() async {
+ const codeContent = codeThatWrapsAt40;
+ const optionsContent = '''
+formatter:
+ page_width: 40
+''';
+
+ newFile(analysisOptionsPath, optionsContent);
+ newFile(mainFilePath, codeContent);
+ await provideConfig(
+ initialize,
+ {
+ // This won't apply because analysis_options wins.
+ 'lineLength': 100,
+ },
+ );
+
+ // Ignore trailing newlines when checking for wrapping.
+ var formatted = await formatContents(mainFileUri, codeContent);
+ expect(formatted.trim(), contains('\n'));
+ }
+
Future<void> test_lineLength_outsideWorkspaceFolders() async {
const contents = '''
void f() {
diff --git a/pkg/analysis_server/test/src/services/completion/yaml/analysis_options_generator_test.dart b/pkg/analysis_server/test/src/services/completion/yaml/analysis_options_generator_test.dart
index 8a05a37..51c80e8 100644
--- a/pkg/analysis_server/test/src/services/completion/yaml/analysis_options_generator_test.dart
+++ b/pkg/analysis_server/test/src/services/completion/yaml/analysis_options_generator_test.dart
@@ -128,11 +128,20 @@
getCompletions('^');
assertSuggestion('${AnalyzerOptions.analyzer}: ');
assertSuggestion('${AnalyzerOptions.codeStyle}: ');
+ assertSuggestion('${AnalyzerOptions.formatter}: ');
assertSuggestion('${AnalyzerOptions.include}: ');
// TODO(brianwilkerson): Replace this with a constant.
assertSuggestion('linter: ');
}
+ void test_formatter() {
+ getCompletions('''
+formatter:
+ ^
+''');
+ assertSuggestion('${AnalyzerOptions.pageWidth}: ');
+ }
+
void test_linter() {
getCompletions('''
linter:
diff --git a/pkg/analysis_server/tool/lsp_spec/README.md b/pkg/analysis_server/tool/lsp_spec/README.md
index 05abbef..181649c 100644
--- a/pkg/analysis_server/tool/lsp_spec/README.md
+++ b/pkg/analysis_server/tool/lsp_spec/README.md
@@ -36,7 +36,7 @@
- `dart.analysisExcludedFolders` (`List<String>?`): An array of paths (absolute or relative to each workspace folder) that should be excluded from analysis.
- `dart.enableSdkFormatter` (`bool?`): When set to `false`, prevents registration (or unregisters) the SDK formatter. When set to `true` or not supplied, will register/reregister the SDK formatter.
-- `dart.lineLength` (`int?`): The number of characters the formatter should wrap code at. If unspecified, code will be wrapped at `80` characters.
+- `dart.lineLength` (`int?`): Sets a default value for the formatter to wrap code at if no value is specified in `formatter.page_width` in `analysis_options.yaml`. If unspecified by both, code will be wrapped at `80` characters.
- `dart.completeFunctionCalls` (`bool?`): When set to true, completes functions/methods with their required parameters.
- `dart.showTodos` (`bool?`): Whether to generate diagnostics for TODO comments. If unspecified, diagnostics will not be generated.
- `dart.renameFilesWithClasses` (`String`): When set to `"always"`, will include edits to rename files when classes are renamed if the filename matches the class name (but in snake_form). When set to `"prompt"`, a prompt will be shown on each class rename asking to confirm the file rename. Otherwise, files will not be renamed. Renames are performed using LSP's ResourceOperation edits - that means the rename is simply included in the resulting `WorkspaceEdit` and must be handled by the client.
diff --git a/pkg/analyzer/lib/dart/analysis/analysis_options.dart b/pkg/analyzer/lib/dart/analysis/analysis_options.dart
index 84c11ff..93ea731 100644
--- a/pkg/analyzer/lib/dart/analysis/analysis_options.dart
+++ b/pkg/analyzer/lib/dart/analysis/analysis_options.dart
@@ -4,6 +4,7 @@
import 'package:analyzer/dart/analysis/code_style_options.dart';
import 'package:analyzer/dart/analysis/features.dart';
+import 'package:analyzer/dart/analysis/formatter_options.dart';
import 'package:analyzer/source/error_processor.dart';
import 'package:analyzer/src/lint/linter.dart';
import 'package:pub_semver/src/version_constraint.dart';
@@ -38,6 +39,9 @@
/// analysis.
List<String> get excludePatterns;
+ /// Return the options used to control the formatter.
+ FormatterOptions get formatterOptions;
+
/// Return `true` if analysis is to generate hint results (e.g. best practices
/// and analysis based on certain annotations).
@Deprecated("Use 'warning' instead")
diff --git a/pkg/analyzer/lib/dart/analysis/formatter_options.dart b/pkg/analyzer/lib/dart/analysis/formatter_options.dart
new file mode 100644
index 0000000..a6837fd9
--- /dev/null
+++ b/pkg/analyzer/lib/dart/analysis/formatter_options.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2024, 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.
+
+/// A set of options related to the formatter that apply to the code within a
+/// single analysis context.
+///
+/// Clients may not extend, implement or mix-in this class.
+abstract class FormatterOptions {
+ /// The width configured for where the formatter should wrap code.
+ int? get pageWidth;
+}
diff --git a/pkg/analyzer/lib/src/analysis_options/apply_options.dart b/pkg/analyzer/lib/src/analysis_options/apply_options.dart
index e266feb..6afa907 100644
--- a/pkg/analyzer/lib/src/analysis_options/apply_options.dart
+++ b/pkg/analyzer/lib/src/analysis_options/apply_options.dart
@@ -4,9 +4,11 @@
import 'package:analyzer/dart/analysis/code_style_options.dart';
import 'package:analyzer/dart/analysis/features.dart';
+import 'package:analyzer/dart/analysis/formatter_options.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/source/error_processor.dart';
import 'package:analyzer/src/analysis_options/code_style_options.dart';
+import 'package:analyzer/src/analysis_options/formatter_options.dart';
import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/utilities_general.dart';
@@ -148,6 +150,18 @@
return CodeStyleOptionsImpl(this, useFormatter: useFormatter);
}
+ FormatterOptions buildFormatterOptions(YamlNode? formatter) {
+ int? pageWidth;
+ if (formatter is YamlMap) {
+ var formatNode = formatter.valueAt(AnalyzerOptions.pageWidth);
+ var formatValue = formatNode?.value;
+ if (formatValue is int && formatValue > 0) {
+ pageWidth = formatValue;
+ }
+ }
+ return FormatterOptionsImpl(this, pageWidth: pageWidth);
+ }
+
void _applyLegacyPlugins(YamlNode? plugins) {
var pluginName = plugins.stringValue;
if (pluginName != null) {
@@ -226,6 +240,10 @@
var codeStyle = optionMap.valueAt(AnalyzerOptions.codeStyle);
codeStyleOptions = buildCodeStyleOptions(codeStyle);
+ // Process the 'formatter' option.
+ var formatter = optionMap.valueAt(AnalyzerOptions.formatter);
+ formatterOptions = buildFormatterOptions(formatter);
+
var config = parseConfig(optionMap);
if (config != null) {
var enabledRules = Registry.ruleRegistry.enabled(config);
diff --git a/pkg/analyzer/lib/src/analysis_options/formatter_options.dart b/pkg/analyzer/lib/src/analysis_options/formatter_options.dart
new file mode 100644
index 0000000..e2352eb
--- /dev/null
+++ b/pkg/analyzer/lib/src/analysis_options/formatter_options.dart
@@ -0,0 +1,18 @@
+// Copyright (c) 2024, 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 'package:analyzer/dart/analysis/analysis_options.dart';
+import 'package:analyzer/dart/analysis/formatter_options.dart';
+
+/// The concrete implementation of [FormatterOptions].
+class FormatterOptionsImpl implements FormatterOptions {
+ /// The analysis options that owns this instance.
+ final AnalysisOptions options;
+
+ /// The width configured for where the formatter should wrap code.
+ @override
+ final int? pageWidth;
+
+ FormatterOptionsImpl(this.options, {this.pageWidth});
+}
diff --git a/pkg/analyzer/lib/src/generated/engine.dart b/pkg/analyzer/lib/src/generated/engine.dart
index caf41d6..3950a30 100644
--- a/pkg/analyzer/lib/src/generated/engine.dart
+++ b/pkg/analyzer/lib/src/generated/engine.dart
@@ -9,6 +9,7 @@
import 'package:analyzer/dart/analysis/code_style_options.dart';
import 'package:analyzer/dart/analysis/declared_variables.dart';
import 'package:analyzer/dart/analysis/features.dart';
+import 'package:analyzer/dart/analysis/formatter_options.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/instrumentation/instrumentation.dart';
@@ -16,6 +17,7 @@
import 'package:analyzer/source/line_info.dart';
import 'package:analyzer/source/source.dart';
import 'package:analyzer/src/analysis_options/code_style_options.dart';
+import 'package:analyzer/src/analysis_options/formatter_options.dart';
import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:analyzer/src/generated/source.dart' show SourceFactory;
import 'package:analyzer/src/lint/linter.dart';
@@ -239,6 +241,9 @@
@override
late CodeStyleOptions codeStyleOptions;
+ @override
+ late FormatterOptions formatterOptions;
+
/// The set of "un-ignorable" error names, as parsed in [AnalyzerOptions] from
/// an analysis options file.
Set<String> unignorableNames = {};
@@ -247,12 +252,14 @@
/// values.
AnalysisOptionsImpl({this.file}) {
codeStyleOptions = CodeStyleOptionsImpl(this, useFormatter: false);
+ formatterOptions = FormatterOptionsImpl(this);
}
/// Initialize a newly created set of analysis options to have the same values
/// as those in the given set of analysis [options].
AnalysisOptionsImpl.from(AnalysisOptions options) {
codeStyleOptions = options.codeStyleOptions;
+ formatterOptions = options.formatterOptions;
contextFeatures = options.contextFeatures;
enabledLegacyPluginNames = options.enabledLegacyPluginNames;
errorProcessors = options.errorProcessors;
diff --git a/pkg/analyzer/lib/src/task/options.dart b/pkg/analyzer/lib/src/task/options.dart
index 8b592db..bf87e60 100644
--- a/pkg/analyzer/lib/src/task/options.dart
+++ b/pkg/analyzer/lib/src/task/options.dart
@@ -239,6 +239,7 @@
static const String enableExperiment = 'enable-experiment';
static const String errors = 'errors';
static const String exclude = 'exclude';
+ static const String formatter = 'formatter';
static const String include = 'include';
static const String language = 'language';
static const String optionalChecks = 'optional-checks';
@@ -270,6 +271,9 @@
/// Ways to say `include`.
static const List<String> includeSynonyms = ['include', 'true'];
+ // Formatter options.
+ static const String pageWidth = 'page_width';
+
static const String propagateLinterExceptions = 'propagate-linter-exceptions';
/// Ways to say `true` or `false`.
@@ -648,6 +652,52 @@
}
}
+/// Validates `formatter` options.
+class FormatterOptionsValidator extends OptionsValidator {
+ @override
+ void validate(ErrorReporter reporter, YamlMap options) {
+ var formatter = options.valueAt(AnalyzerOptions.formatter);
+ if (formatter is YamlMap) {
+ for (var MapEntry(key: keyNode, value: valueNode)
+ in formatter.nodeMap.entries) {
+ if (keyNode.value == AnalyzerOptions.pageWidth) {
+ _validatePageWidth(keyNode, valueNode, reporter);
+ } else {
+ reporter.atSourceSpan(
+ keyNode.span,
+ AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITHOUT_VALUES,
+ arguments: [
+ AnalyzerOptions.formatter,
+ keyNode.toString(),
+ ],
+ );
+ }
+ }
+ } else if (formatter != null && formatter.value != null) {
+ reporter.atSourceSpan(
+ formatter.span,
+ AnalysisOptionsWarningCode.INVALID_SECTION_FORMAT,
+ arguments: [AnalyzerOptions.formatter],
+ );
+ }
+ }
+
+ void _validatePageWidth(
+ YamlNode keyNode, YamlNode valueNode, ErrorReporter reporter) {
+ var value = valueNode.value;
+ if (value is! int || value <= 0) {
+ reporter.atSourceSpan(
+ valueNode.span,
+ AnalysisOptionsWarningCode.INVALID_OPTION,
+ arguments: [
+ keyNode.toString(),
+ '"page_width" must be a positive integer.',
+ ],
+ );
+ }
+ }
+}
+
/// Validates `analyzer` language configuration options.
class LanguageOptionValidator extends OptionsValidator {
final ErrorBuilder _builder = ErrorBuilder(AnalyzerOptions.languageOptions);
@@ -776,6 +826,7 @@
}) : _validators = [
AnalyzerOptionsValidator(),
CodeStyleOptionsValidator(),
+ FormatterOptionsValidator(),
LinterOptionsValidator(),
LinterRuleOptionsValidator(
provider: provider,
diff --git a/pkg/analyzer/test/src/task/options_test.dart b/pkg/analyzer/test/src/task/options_test.dart
index 0b0524f..956fac7 100644
--- a/pkg/analyzer/test/src/task/options_test.dart
+++ b/pkg/analyzer/test/src/task/options_test.dart
@@ -332,6 +332,73 @@
''', [AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITHOUT_VALUES]);
}
+ test_formatter_invalid_key() {
+ validate('''
+formatter:
+ wrong: 123
+''', [
+ AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITHOUT_VALUES,
+ ]);
+ }
+
+ test_formatter_invalid_keys() {
+ validate('''
+formatter:
+ wrong: 123
+ wrong2: 123
+''', [
+ AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITHOUT_VALUES,
+ AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITHOUT_VALUES,
+ ]);
+ }
+
+ test_formatter_pageWidth_invalid_decimal() {
+ validate('''
+formatter:
+ page_width: 123.45
+''', [
+ AnalysisOptionsWarningCode.INVALID_OPTION,
+ ]);
+ }
+
+ test_formatter_pageWidth_invalid_negativeInteger() {
+ validate('''
+formatter:
+ page_width: -123
+''', [
+ AnalysisOptionsWarningCode.INVALID_OPTION,
+ ]);
+ }
+
+ test_formatter_pageWidth_invalid_string() {
+ validate('''
+formatter:
+ page_width: "123"
+''', [AnalysisOptionsWarningCode.INVALID_OPTION]);
+ }
+
+ test_formatter_pageWidth_invalid_zero() {
+ validate('''
+formatter:
+ page_width: 0
+''', [
+ AnalysisOptionsWarningCode.INVALID_OPTION,
+ ]);
+ }
+
+ test_formatter_pageWidth_valid_integer() {
+ validate('''
+formatter:
+ page_width: 123
+''', []);
+ }
+
+ test_formatter_valid_empty() {
+ validate('''
+formatter:
+''', []);
+ }
+
test_linter_supported_rules() {
Registry.ruleRegistry.register(TestRule());
validate('''