@@ -7,21 +7,34 @@ import 'package:analysis_server_plugin/edit/dart/correction_producer.dart';
77import 'package:analyzer/dart/ast/ast.dart' ;
88import 'package:analyzer/dart/element/element.dart' ;
99import 'package:analyzer/src/generated/error_verifier.dart' ;
10- import 'package:analyzer/src/utilities/extensions/flutter.dart' ;
1110import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart' ;
1211import 'package:analyzer_plugin/utilities/fixes/fixes.dart' ;
1312
13+ /// The boolean value indicates whether the field is required.
14+ /// The string value is the field/parameter name.
15+ typedef _FieldRecord = ({bool isRequired, String parameter});
16+
1417class AddFieldFormalParameters extends ResolvedCorrectionProducer {
15- AddFieldFormalParameters ({required super .context});
18+ final _Style _style;
19+
20+ bool _useRequired = false ;
21+
22+ @override
23+ final FixKind fixKind;
24+
25+ AddFieldFormalParameters ({required super .context})
26+ : _style = _Style .base ,
27+ fixKind = DartFixKind .ADD_INITIALIZING_FORMAL_PARAMETERS ;
28+
29+ AddFieldFormalParameters .requiredNamed ({required super .context})
30+ : _style = _Style .requiredNamed,
31+ fixKind = DartFixKind .ADD_INITIALIZING_FORMAL_NAMED_PARAMETERS ;
1632
1733 @override
1834 CorrectionApplicability get applicability =>
1935 // TODO(applicability): comment on why.
2036 CorrectionApplicability .singleLocation;
2137
22- @override
23- FixKind get fixKind => DartFixKind .ADD_INITIALIZING_FORMAL_PARAMETERS ;
24-
2538 @override
2639 Future <void > compute (ChangeBuilder builder) async {
2740 var constructor = node.parent;
@@ -47,43 +60,73 @@ class AddFieldFormalParameters extends ResolvedCorrectionProducer {
4760 (a, b) => a.firstFragment.nameOffset! - b.firstFragment.nameOffset! ,
4861 );
4962
50- // Specialize for Flutter widgets.
51- if (superType.isExactlyStatelessWidgetType ||
52- superType.isExactlyStatefulWidgetType) {
53- if (parameters.isNotEmpty && parameters.last.isNamed) {
54- String parameterForField (FieldElement field) {
55- var prefix = '' ;
56- if (typeSystem.isPotentiallyNonNullable (field.type)) {
57- prefix = 'required ' ;
58- }
59- return '${prefix }this.${field .name }' ;
60- }
61-
62- var fieldParametersCode = fields.map (parameterForField).join (', ' );
63- await builder.addDartFileEdit (file, (builder) {
64- builder.addSimpleInsertion (
65- parameters.last.end,
66- ', $fieldParametersCode ' ,
67- );
68- });
69- return ;
70- }
71- }
72-
7363 // Prepare the last required parameter.
7464 FormalParameter ? lastRequiredParameter;
65+ FormalParameter ? firstNamedParameter;
7566 for (var parameter in parameters) {
7667 if (parameter.isRequiredPositional) {
7768 lastRequiredParameter = parameter;
69+ } else if (_style == _Style .base ) {
70+ break ;
71+ } else if (parameter.isOptionalPositional) {
72+ // If there are optional positional parameters, we can't add required
73+ // named parameters.
74+ return ;
75+ } else if (parameter.isNamed) {
76+ firstNamedParameter = parameter;
77+ break ;
7878 }
7979 }
8080
81- var fieldParametersCode = fields
82- .map ((field) => 'this.${field .name }' )
83- .join (', ' );
81+ if (_style == _Style .requiredNamed) {
82+ _useRequired = true ;
83+ }
84+
85+ var fieldsRecords = fields.map (_parameterForField).toList ();
86+ var requiredFirst =
87+ getCodeStyleOptions (unitResult.file).requiredNamedParametersFirst;
88+ if (requiredFirst) {
89+ fieldsRecords.sort ((a, b) {
90+ if (a.isRequired && ! b.isRequired) {
91+ return - 1 ;
92+ } else if (! a.isRequired && b.isRequired) {
93+ return 1 ;
94+ }
95+ return a.parameter.compareTo (b.parameter);
96+ });
97+ }
98+ var requiredParameters = fieldsRecords.where ((r) => r.isRequired);
99+ var optionalParameters = fieldsRecords
100+ .where ((r) => ! r.isRequired)
101+ .map ((r) => r.parameter);
102+ var fieldParametersCode = fieldsRecords.map ((r) => r.parameter).join (', ' );
84103 await builder.addDartFileEdit (file, (builder) {
85- if (lastRequiredParameter != null ) {
104+ if (firstNamedParameter != null &&
105+ requiredFirst &&
106+ requiredParameters.isNotEmpty) {
86107 builder.addSimpleInsertion (
108+ firstNamedParameter.offset,
109+ '${requiredParameters .map ((r ) => r .parameter ).join (', ' )}, ' ,
110+ );
111+ if (optionalParameters.isNotEmpty) {
112+ fieldParametersCode = optionalParameters.join (', ' );
113+ } else {
114+ return ; // No optional parameters to add.
115+ }
116+ }
117+ if (_style == _Style .requiredNamed) {
118+ var lastParameter = parameters.lastOrNull;
119+ if (lastParameter != null ) {
120+ var write = ', ' ;
121+ if (! lastParameter.isNamed) {
122+ write += '{$fieldParametersCode }' ;
123+ } else {
124+ write += fieldParametersCode;
125+ }
126+ builder.addSimpleInsertion (parameters.last.end, write);
127+ }
128+ } else if (lastRequiredParameter != null ) {
129+ return builder.addSimpleInsertion (
87130 lastRequiredParameter.end,
88131 ', $fieldParametersCode ' ,
89132 );
@@ -96,4 +139,16 @@ class AddFieldFormalParameters extends ResolvedCorrectionProducer {
96139 }
97140 });
98141 }
142+
143+ _FieldRecord _parameterForField (FieldElement field) {
144+ var prefix = '' ;
145+ var isRequired = false ;
146+ if (typeSystem.isPotentiallyNonNullable (field.type) && _useRequired) {
147+ isRequired = true ;
148+ prefix = 'required ' ;
149+ }
150+ return (isRequired: isRequired, parameter: '${prefix }this.${field .name }' );
151+ }
99152}
153+
154+ enum _Style { base , requiredNamed }
0 commit comments