Skip to content

Commit 16d6594

Browse files
DanTupCommit Queue
authored andcommitted
[analysis_server] Wire up analysis_options formatter/trailing_commas to formatter
+ validation + code completion Fixes #60599 Change-Id: I383c888756e29e091c7abf8d4b8df66387714e31 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/424440 Reviewed-by: Konstantin Shcheglov <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Brian Wilkerson <[email protected]>
1 parent 807074b commit 16d6594

File tree

13 files changed

+296
-15
lines changed

13 files changed

+296
-15
lines changed

pkg/analysis_server/lib/src/handler/legacy/edit_format.dart

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@ import 'dart:async';
77
import 'package:analysis_server/protocol/protocol.dart';
88
import 'package:analysis_server/protocol/protocol_generated.dart';
99
import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
10+
import 'package:analysis_server/src/utilities/extensions/formatter_options.dart';
1011
import 'package:analyzer/dart/analysis/results.dart';
1112
import 'package:analyzer_plugin/protocol/protocol_common.dart';
12-
import 'package:dart_style/src/dart_formatter.dart';
13-
import 'package:dart_style/src/exceptions.dart';
14-
import 'package:dart_style/src/source_code.dart';
13+
import 'package:dart_style/dart_style.dart' hide TrailingCommas;
1514

1615
/// The handler for the `edit.format` request.
1716
class EditFormatHandler extends LegacyHandler {
@@ -55,11 +54,13 @@ class EditFormatHandler extends LegacyHandler {
5554
selectionLength: length,
5655
);
5756

58-
var effectivePageWidth =
59-
unit.analysisOptions.formatterOptions.pageWidth ?? params.lineLength;
57+
var formatterOptions = unit.analysisOptions.formatterOptions;
58+
var effectivePageWidth = formatterOptions.pageWidth ?? params.lineLength;
59+
var effectiveTrailingCommas = formatterOptions.dartStyleTrailingCommas;
6060
var effectiveLanguageVersion = unit.unit.languageVersion.effective;
6161
var formatter = DartFormatter(
6262
pageWidth: effectivePageWidth,
63+
trailingCommas: effectiveTrailingCommas,
6364
languageVersion: effectiveLanguageVersion,
6465
);
6566
SourceCode formattedResult;

pkg/analysis_server/lib/src/lsp/source_edits.dart

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:analysis_server/src/lsp/mapping.dart';
88
import 'package:analysis_server/src/protocol_server.dart'
99
as server
1010
show SourceEdit;
11+
import 'package:analysis_server/src/utilities/extensions/formatter_options.dart';
1112
import 'package:analyzer/dart/analysis/features.dart';
1213
import 'package:analyzer/dart/analysis/results.dart';
1314
import 'package:analyzer/dart/ast/token.dart';
@@ -17,7 +18,7 @@ import 'package:analyzer/source/source.dart';
1718
import 'package:analyzer/src/dart/scanner/reader.dart';
1819
import 'package:analyzer/src/dart/scanner/scanner.dart';
1920
import 'package:analyzer_plugin/protocol/protocol_common.dart' as plugin;
20-
import 'package:dart_style/dart_style.dart';
21+
import 'package:dart_style/dart_style.dart' hide TrailingCommas;
2122

2223
/// Checks whether a string contains only characters that are allowed to differ
2324
/// between unformattedformatted code (such as whitespace, commas, semicolons).
@@ -107,10 +108,11 @@ ErrorOr<List<TextEdit>?> generateEditsForFormatting(
107108
}) {
108109
var unformattedSource = result.content;
109110

111+
var formatterOptions = result.analysisOptions.formatterOptions;
110112
// The analysis options page width always takes priority over the default from
111113
// the LSP configuration.
112-
var effectivePageWidth =
113-
result.analysisOptions.formatterOptions.pageWidth ?? defaultPageWidth;
114+
var effectivePageWidth = formatterOptions.pageWidth ?? defaultPageWidth;
115+
var effectiveTrailingCommas = formatterOptions.dartStyleTrailingCommas;
114116
var effectiveLanguageVersion = result.unit.languageVersion.effective;
115117

116118
var code = SourceCode(unformattedSource);
@@ -121,6 +123,7 @@ ErrorOr<List<TextEdit>?> generateEditsForFormatting(
121123
// https://github.com/dart-lang/dart_style/issues/1337
122124
var formatter = DartFormatter(
123125
pageWidth: effectivePageWidth,
126+
trailingCommas: effectiveTrailingCommas,
124127
languageVersion: effectiveLanguageVersion,
125128
);
126129
formattedResult = formatter.formatSource(code);

pkg/analysis_server/lib/src/services/completion/yaml/analysis_options_generator.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import 'package:analysis_server/src/protocol_server.dart';
66
import 'package:analysis_server/src/services/completion/yaml/producer.dart';
77
import 'package:analysis_server/src/services/completion/yaml/yaml_completion_generator.dart';
8+
import 'package:analyzer/dart/analysis/formatter_options.dart';
89
import 'package:analyzer/error/error.dart';
910
import 'package:analyzer/file_system/file_system.dart';
1011
import 'package:analyzer/src/dart/analysis/experiments.dart';
@@ -41,6 +42,9 @@ class AnalysisOptionsGenerator extends YamlCompletionGenerator {
4142
}),
4243
AnalysisOptionsFile.formatter: MapProducer({
4344
AnalysisOptionsFile.pageWidth: EmptyProducer(),
45+
AnalysisOptionsFile.trailingCommas: EnumProducer(
46+
TrailingCommas.values.map((item) => item.name).toList(),
47+
),
4448
}),
4549
// TODO(brianwilkerson): Create a producer to produce `package:` URIs.
4650
AnalysisOptionsFile.include: EmptyProducer(),
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:analyzer/dart/analysis/formatter_options.dart'
6+
hide TrailingCommas;
7+
import 'package:collection/collection.dart';
8+
import 'package:dart_style/dart_style.dart';
9+
10+
extension FormatterOptionsExtension on FormatterOptions {
11+
/// The trailing commas preference converted from the analyzer enum to the
12+
/// dart_style enum.
13+
TrailingCommas? get dartStyleTrailingCommas {
14+
return switch (trailingCommas) {
15+
var trailingCommas? => TrailingCommas.values.firstWhereOrNull(
16+
(item) => item.name == trailingCommas.name,
17+
),
18+
null => null,
19+
};
20+
}
21+
}

pkg/analysis_server/test/domain_completion_test.dart

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2038,6 +2038,32 @@ suggestions
20382038
''');
20392039
}
20402040

2041+
Future<void> test_yaml_analysisOptions_formatter() async {
2042+
await _configureWithWorkspaceRoot();
2043+
2044+
var path = convertPath('$testPackageRootPath/analysis_options.yaml');
2045+
var response = await _getCodeSuggestions(
2046+
path: path,
2047+
content: '''
2048+
formatter:
2049+
^
2050+
''',
2051+
);
2052+
2053+
printerConfiguration
2054+
..filter = ((_) => true)
2055+
..withIsNotImported = false
2056+
..withLibraryUri = false;
2057+
2058+
assertResponseText(response, r'''
2059+
suggestions
2060+
|page_width: |
2061+
kind: identifier
2062+
|trailing_commas: |
2063+
kind: identifier
2064+
''');
2065+
}
2066+
20412067
Future<void> test_yaml_analysisOptions_root() async {
20422068
await _configureWithWorkspaceRoot();
20432069

pkg/analysis_server/test/edit/format_test.dart

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,71 @@ void f() {
174174
expect(formatResult.selectionLength, equals(3));
175175
}
176176

177+
Future<void> test_format_trailingCommas_automate() async {
178+
writeTestPackageAnalysisOptionsFile(r'''
179+
formatter:
180+
trailing_commas: automate
181+
''');
182+
var content = '''
183+
enum A {
184+
a,
185+
b,
186+
}
187+
''';
188+
addTestFile(content);
189+
await waitForTasksFinished();
190+
var formatResult = await _formatAt(0, 3);
191+
192+
expect(
193+
formatResult.edits.single.replacement,
194+
equals('''
195+
enum A { a, b }
196+
'''),
197+
);
198+
}
199+
200+
Future<void> test_format_trailingCommas_preserve() async {
201+
writeTestPackageAnalysisOptionsFile(r'''
202+
formatter:
203+
trailing_commas: preserve
204+
''');
205+
var content = '''
206+
enum A { a, b, }
207+
''';
208+
addTestFile(content);
209+
await waitForTasksFinished();
210+
var formatResult = await _formatAt(0, 3);
211+
212+
expect(
213+
formatResult.edits.single.replacement,
214+
equals('''
215+
enum A {
216+
a,
217+
b,
218+
}
219+
'''),
220+
);
221+
}
222+
223+
Future<void> test_format_trailingCommas_unspecified() async {
224+
var content = '''
225+
enum A {
226+
a,
227+
b,
228+
}
229+
''';
230+
addTestFile(content);
231+
await waitForTasksFinished();
232+
var formatResult = await _formatAt(0, 3);
233+
234+
expect(
235+
formatResult.edits.single.replacement,
236+
equals('''
237+
enum A { a, b }
238+
'''),
239+
);
240+
}
241+
177242
/// Verify version 2.19 is passed to the formatter so it will not produce any
178243
/// edits for code containing records (since it fails to parse).
179244
Future<void> test_format_version_2_19() async {

pkg/analysis_server/test/lsp/format_test.dart

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,73 @@ void f() {
859859
await expectFormattedContents(mainFileUri, contents, expected);
860860
}
861861

862+
Future<void> test_trailingCommas_automate() async {
863+
const optionsContent = '''
864+
formatter:
865+
trailing_commas: automate
866+
''';
867+
const initialContent = '''
868+
enum A {
869+
a,
870+
b,
871+
}
872+
''';
873+
const expectedContent = '''
874+
enum A { a, b }
875+
''';
876+
877+
newFile(analysisOptionsPath, optionsContent);
878+
newFile(mainFilePath, initialContent);
879+
await initialize();
880+
881+
// Ignore trailing newlines when checking for wrapping.
882+
var formatted = await formatContents(mainFileUri, initialContent);
883+
expect(formatted.trim(), expectedContent.trim());
884+
}
885+
886+
Future<void> test_trailingCommas_preserve() async {
887+
const optionsContent = '''
888+
formatter:
889+
trailing_commas: preserve
890+
''';
891+
const initialContent = '''
892+
enum A { a, b, }
893+
''';
894+
const expectedContent = '''
895+
enum A {
896+
a,
897+
b,
898+
}
899+
''';
900+
901+
newFile(analysisOptionsPath, optionsContent);
902+
newFile(mainFilePath, initialContent);
903+
await initialize();
904+
905+
// Ignore trailing newlines when checking for wrapping.
906+
var formatted = await formatContents(mainFileUri, initialContent);
907+
expect(formatted.trim(), expectedContent.trim());
908+
}
909+
910+
Future<void> test_trailingCommas_unspecified() async {
911+
const initialContent = '''
912+
enum A {
913+
a,
914+
b,
915+
}
916+
''';
917+
const expectedContent = '''
918+
enum A { a, b }
919+
''';
920+
921+
newFile(mainFilePath, initialContent);
922+
await initialize();
923+
924+
// Ignore trailing newlines when checking for wrapping.
925+
var formatted = await formatContents(mainFileUri, initialContent);
926+
expect(formatted.trim(), expectedContent.trim());
927+
}
928+
862929
Future<void> test_unopenFile() async {
863930
const contents = '''
864931
void f ()

pkg/analysis_server/test/src/services/completion/yaml/analysis_options_generator_test.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,16 @@ formatter:
161161
^
162162
''');
163163
assertSuggestion('${AnalysisOptionsFile.pageWidth}: ');
164+
assertSuggestion('${AnalysisOptionsFile.trailingCommas}: ');
165+
}
166+
167+
void test_formatter_trailingCommas() {
168+
getCompletions('''
169+
formatter:
170+
trailing_commas: ^
171+
''');
172+
assertSuggestion('automate');
173+
assertSuggestion('preserve');
164174
}
165175

166176
void test_linter() {

pkg/analyzer/api.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,13 @@ package:analyzer/dart/analysis/features.dart:
141141
values (static getter: List<FeatureStatus>)
142142
package:analyzer/dart/analysis/formatter_options.dart:
143143
FormatterOptions (class extends Object):
144-
new (constructor: FormatterOptions Function({int? pageWidth}))
144+
new (constructor: FormatterOptions Function({int? pageWidth, TrailingCommas? trailingCommas}))
145145
pageWidth (getter: int?)
146+
trailingCommas (getter: TrailingCommas?)
147+
TrailingCommas (enum):
148+
automate (static getter: TrailingCommas)
149+
preserve (static getter: TrailingCommas)
150+
values (static getter: List<TrailingCommas>)
146151
package:analyzer/dart/analysis/results.dart:
147152
AnalysisResult (class extends Object):
148153
new (constructor: AnalysisResult Function())

pkg/analyzer/lib/dart/analysis/formatter_options.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,11 @@ final class FormatterOptions {
88
/// The width configured for where the formatter should wrap code.
99
final int? pageWidth;
1010

11-
FormatterOptions({this.pageWidth});
11+
/// How trailing commas in various constructs should affect formatting.
12+
final TrailingCommas? trailingCommas;
13+
14+
FormatterOptions({this.pageWidth, this.trailingCommas});
1215
}
16+
17+
/// How trailing commas in various constructs should affect formatting.
18+
enum TrailingCommas { automate, preserve }

0 commit comments

Comments
 (0)