[analysis_server] Pass experiments through to formatter
This passes any experiments that are both enabled, and marked as "future" through to the formatter. I extracted most uses of the formatter to use the same shared `createFormatter()` helper that sets the appropriate values. Those that didn't already have a Result to pass I added TODOs to (though one is g3 so probably would require some internal migration). See https://github.com/dart-lang/sdk/issues/55125 Change-Id: I8f4ef4242614dc240e217cdaf99105f2a5b49dc9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/486840 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Samuel Rawlins <srawlins@google.com> Reviewed-by: Samuel Rawlins <srawlins@google.com>
This commit is contained in:
committed by
Commit Queue
parent
38743dd774
commit
4a4658e014
@@ -7,9 +7,9 @@ import 'dart:async';
|
||||
import 'package:analysis_server/protocol/protocol.dart';
|
||||
import 'package:analysis_server/protocol/protocol_generated.dart';
|
||||
import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
|
||||
import 'package:analysis_server/src/utilities/extensions/formatter_options.dart';
|
||||
import 'package:analyzer/dart/analysis/results.dart';
|
||||
import 'package:analyzer_plugin/protocol/protocol_common.dart';
|
||||
import 'package:analyzer_plugin/src/utilities/formatter.dart';
|
||||
import 'package:dart_style/dart_style.dart' hide TrailingCommas;
|
||||
|
||||
/// The handler for the `edit.format` request.
|
||||
@@ -47,25 +47,17 @@ class EditFormatHandler extends LegacyHandler {
|
||||
length = null;
|
||||
}
|
||||
|
||||
var formatter = createFormatter(unit, defaultPageWidth: params.lineLength);
|
||||
var unformattedCode = unit.content;
|
||||
var code = SourceCode(
|
||||
unformattedCode,
|
||||
selectionStart: start,
|
||||
selectionLength: length,
|
||||
);
|
||||
|
||||
var formatterOptions = unit.analysisOptions.formatterOptions;
|
||||
var effectivePageWidth = formatterOptions.pageWidth ?? params.lineLength;
|
||||
var effectiveTrailingCommas = formatterOptions.dartStyleTrailingCommas;
|
||||
var effectiveLanguageVersion = unit.unit.languageVersion.effective;
|
||||
var formatter = DartFormatter(
|
||||
pageWidth: effectivePageWidth,
|
||||
trailingCommas: effectiveTrailingCommas,
|
||||
languageVersion: effectiveLanguageVersion,
|
||||
);
|
||||
SourceCode formattedResult;
|
||||
try {
|
||||
formattedResult = formatter.formatSource(code);
|
||||
formattedResult = formatter.formatSource(
|
||||
SourceCode(
|
||||
unformattedCode,
|
||||
selectionStart: start,
|
||||
selectionLength: length,
|
||||
),
|
||||
);
|
||||
} on FormatterException {
|
||||
sendResponse(Response.formatWithErrors(request));
|
||||
return;
|
||||
|
||||
@@ -36,6 +36,9 @@ class EditFormatIfEnabledHandler extends LegacyHandler {
|
||||
var originalContent = file.readAsStringSync();
|
||||
var code = SourceCode(originalContent);
|
||||
|
||||
// TODO(dantup): Consider using createFormatter() which takes a result and
|
||||
// correctly applies settings like page_width, trailing_commas, and enabled
|
||||
// experiments.
|
||||
var formatter = DartFormatter(
|
||||
languageVersion: languageVersion ?? DartFormatter.latestLanguageVersion,
|
||||
);
|
||||
|
||||
@@ -8,7 +8,6 @@ import 'package:analysis_server/src/lsp/mapping.dart';
|
||||
import 'package:analysis_server/src/protocol_server.dart'
|
||||
as server
|
||||
show SourceEdit;
|
||||
import 'package:analysis_server/src/utilities/extensions/formatter_options.dart';
|
||||
import 'package:analyzer/dart/analysis/features.dart';
|
||||
import 'package:analyzer/dart/analysis/results.dart';
|
||||
import 'package:analyzer/dart/ast/token.dart';
|
||||
@@ -17,6 +16,7 @@ import 'package:analyzer/source/line_info.dart';
|
||||
import 'package:analyzer/source/source.dart';
|
||||
import 'package:analyzer/src/dart/scanner/scanner.dart';
|
||||
import 'package:analyzer_plugin/protocol/protocol_common.dart' as plugin;
|
||||
import 'package:analyzer_plugin/src/utilities/formatter.dart';
|
||||
import 'package:dart_style/dart_style.dart' hide TrailingCommas;
|
||||
|
||||
/// Checks whether a string contains only characters that are allowed to differ
|
||||
@@ -130,25 +130,10 @@ ErrorOr<List<TextEdit>?> generateEditsForFormatting(
|
||||
}) {
|
||||
var unformattedSource = result.content;
|
||||
|
||||
var formatterOptions = result.analysisOptions.formatterOptions;
|
||||
// The analysis options page width always takes priority over the default from
|
||||
// the LSP configuration.
|
||||
var effectivePageWidth = formatterOptions.pageWidth ?? defaultPageWidth;
|
||||
var effectiveTrailingCommas = formatterOptions.dartStyleTrailingCommas;
|
||||
var effectiveLanguageVersion = result.unit.languageVersion.effective;
|
||||
|
||||
var code = SourceCode(unformattedSource);
|
||||
SourceCode formattedResult;
|
||||
var formatter = createFormatter(result, defaultPageWidth: defaultPageWidth);
|
||||
String formattedSource;
|
||||
try {
|
||||
// Create a new formatter on every request because it may contain state that
|
||||
// affects repeated formats.
|
||||
// https://github.com/dart-lang/dart_style/issues/1337
|
||||
var formatter = DartFormatter(
|
||||
pageWidth: effectivePageWidth,
|
||||
trailingCommas: effectiveTrailingCommas,
|
||||
languageVersion: effectiveLanguageVersion,
|
||||
);
|
||||
formattedResult = formatter.formatSource(code);
|
||||
formattedSource = formatter.format(unformattedSource);
|
||||
} 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
|
||||
@@ -156,7 +141,6 @@ ErrorOr<List<TextEdit>?> generateEditsForFormatting(
|
||||
// we may wish to change this to return an error for that case).
|
||||
return success(null);
|
||||
}
|
||||
var formattedSource = formattedResult.text;
|
||||
|
||||
if (formattedSource == unformattedSource) {
|
||||
return success(null);
|
||||
|
||||
@@ -23,6 +23,7 @@ import 'package:analyzer_plugin/src/utilities/change_builder/change_builder_core
|
||||
import 'package:analyzer_plugin/src/utilities/charcodes.dart';
|
||||
import 'package:analyzer_plugin/src/utilities/directive_sort.dart';
|
||||
import 'package:analyzer_plugin/src/utilities/extensions/resolved_unit_result.dart';
|
||||
import 'package:analyzer_plugin/src/utilities/formatter.dart';
|
||||
import 'package:analyzer_plugin/src/utilities/library.dart';
|
||||
import 'package:analyzer_plugin/src/utilities/string_utilities.dart';
|
||||
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
|
||||
@@ -1895,16 +1896,14 @@ class DartFileEditBuilderImpl extends FileEditBuilderImpl
|
||||
}
|
||||
}
|
||||
|
||||
var languageVersion = resolvedUnit.libraryElement.languageVersion.effective;
|
||||
var formattedResult = DartFormatter(languageVersion: languageVersion)
|
||||
.formatSource(
|
||||
SourceCode(
|
||||
newContent,
|
||||
isCompilationUnit: true,
|
||||
selectionStart: newRangeOffset,
|
||||
selectionLength: newRangeLength,
|
||||
),
|
||||
);
|
||||
var formatter = createFormatter(resolvedUnit);
|
||||
var formattedResult = formatter.formatSource(
|
||||
SourceCode(
|
||||
newContent,
|
||||
selectionStart: newRangeOffset,
|
||||
selectionLength: newRangeLength,
|
||||
),
|
||||
);
|
||||
|
||||
replaceEdits(
|
||||
range,
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) 2026, 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/features.dart';
|
||||
import 'package:analyzer/dart/analysis/results.dart';
|
||||
import 'package:analyzer/src/dart/analysis/experiments.dart'
|
||||
show ExperimentStatus;
|
||||
import 'package:analyzer_plugin/src/utilities/extensions/formatter_options.dart';
|
||||
import 'package:dart_style/dart_style.dart';
|
||||
|
||||
/// A list of all features that are currently enabled by an experiment flag.
|
||||
final _allowedExperiments = ExperimentStatus.knownFeatures.values
|
||||
.where((feature) => feature.status == FeatureStatus.future)
|
||||
.toList();
|
||||
|
||||
/// Creates a formatter with the appropriate settings for [result].
|
||||
DartFormatter createFormatter(
|
||||
ParsedUnitResult result, {
|
||||
int? defaultPageWidth,
|
||||
}) {
|
||||
var featureSet = result.unit.featureSet;
|
||||
var formatterOptions = result.analysisOptions.formatterOptions;
|
||||
var effectivePageWidth = formatterOptions.pageWidth ?? defaultPageWidth;
|
||||
var effectiveTrailingCommas = formatterOptions.dartStyleTrailingCommas;
|
||||
var effectiveLanguageVersion = result.unit.languageVersion.effective;
|
||||
return DartFormatter(
|
||||
pageWidth: effectivePageWidth,
|
||||
trailingCommas: effectiveTrailingCommas,
|
||||
languageVersion: effectiveLanguageVersion,
|
||||
experimentFlags: _getExperiments(featureSet),
|
||||
);
|
||||
}
|
||||
|
||||
/// Gets the list of experiment strings enabled by [featureSet] that are
|
||||
/// required for future features.
|
||||
List<String> _getExperiments(FeatureSet featureSet) {
|
||||
return _allowedExperiments
|
||||
.where(featureSet.isEnabled)
|
||||
.map((feature) => feature.enableString)
|
||||
.toList();
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright (c) 2026, 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_plugin/src/utilities/formatter.dart';
|
||||
import 'package:analyzer_testing/experiments/experiments.dart';
|
||||
import 'package:dart_style/dart_style.dart';
|
||||
import 'package:pub_semver/pub_semver.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import '../../support/abstract_single_unit.dart';
|
||||
|
||||
void main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(FormatterTest);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class FormatterTest extends AbstractSingleUnitTest {
|
||||
Future<void> test_experiments() async {
|
||||
await resolveTestCode('');
|
||||
var formatter = createFormatter(result);
|
||||
expect(formatter.experimentFlags, experimentsForTests);
|
||||
}
|
||||
|
||||
Future<void> test_languageVersion_default() async {
|
||||
await resolveTestCode('');
|
||||
var formatter = createFormatter(result);
|
||||
expect(formatter.languageVersion, DartFormatter.latestLanguageVersion);
|
||||
}
|
||||
|
||||
Future<void> test_languageVersion_override() async {
|
||||
await resolveTestCode('// @dart=2.12');
|
||||
var formatter = createFormatter(result);
|
||||
expect(formatter.languageVersion, Version(2, 12, 0));
|
||||
}
|
||||
|
||||
Future<void> test_pageWidth() async {
|
||||
newFile(convertPath('$testPackageRootPath/analysis_options.yaml'), '''
|
||||
formatter:
|
||||
page_width: 123
|
||||
''');
|
||||
await resolveTestCode('');
|
||||
var formatter = createFormatter(result);
|
||||
expect(formatter.pageWidth, 123);
|
||||
}
|
||||
|
||||
Future<void> test_trailingCommas() async {
|
||||
newFile(convertPath('$testPackageRootPath/analysis_options.yaml'), '''
|
||||
formatter:
|
||||
trailing_commas: preserve
|
||||
''');
|
||||
await resolveTestCode('');
|
||||
var formatter = createFormatter(result);
|
||||
expect(formatter.trailingCommas, TrailingCommas.preserve);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
import 'change_builder/test_all.dart' as change_builder;
|
||||
import 'client_uri_converter_test.dart' as client_uri_converter;
|
||||
import 'completion/test_all.dart' as completion;
|
||||
import 'formatter_test.dart' as formatter;
|
||||
import 'navigation/test_all.dart' as navigation;
|
||||
import 'string_utilities_test.dart' as string_utilities;
|
||||
import 'visitors/test_all.dart' as visitors;
|
||||
@@ -16,6 +17,7 @@ void main() {
|
||||
change_builder.main();
|
||||
client_uri_converter.main();
|
||||
completion.main();
|
||||
formatter.main();
|
||||
navigation.main();
|
||||
string_utilities.main();
|
||||
visitors.main();
|
||||
|
||||
Reference in New Issue
Block a user