@@ -11,11 +11,11 @@ import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dar
1111import 'package:analyzer_plugin/utilities/fixes/fixes.dart' ;
1212import 'package:collection/collection.dart' ;
1313
14- /// The boolean value indicates whether the field is required.
15- /// The string value is the field/parameter name.
16- typedef _FieldRecord = ({bool isRequired, String parameter});
17- 1814class AddFieldFormalParameters extends ResolvedCorrectionProducer {
15+ static final _charAfterUnderscore = RegExp ('[^_]' );
16+ 17+ static final _startsWithNumber = RegExp ('^[0-9]' );
18+ 1919 final _Style _style;
2020
2121 @override
@@ -27,8 +27,7 @@ class AddFieldFormalParameters extends ResolvedCorrectionProducer {
2727
2828 AddFieldFormalParameters .requiredNamed ({required super .context})
2929 : _style = _Style .requiredNamed,
30- fixKind = DartFixKind .addInitializingFormalNamesParameters;
31- 30+ fixKind = DartFixKind .addInitializingFormalNamedParameters;
3231 @override
3332 CorrectionApplicability get applicability =>
3433 // TODO(applicability): comment on why.
@@ -61,96 +60,149 @@ class AddFieldFormalParameters extends ResolvedCorrectionProducer {
6160 .map ((pair) => pair.1ドル)
6261 .toList ();
6362
63+ if (fields.isEmpty) {
64+ assert (false , 'How can this trigger with no fields?' );
65+ return ;
66+ }
67+ 6468 // Prepare the last required parameter.
65- FormalParameter ? lastRequiredParameter ;
69+ FormalParameter ? lastRequiredPositionalParameter ;
6670 FormalParameter ? firstNamedParameter;
71+ var containsOptionalPositional = false ;
6772 for (var parameter in parameters) {
6873 if (parameter.isRequiredPositional) {
69- lastRequiredParameter = parameter;
70- } else if (_style == _Style .base ) {
71- break ;
74+ lastRequiredPositionalParameter = parameter;
7275 } else if (parameter.isOptionalPositional) {
73- // If there are optional positional parameters, we can't add required
74- // named parameters.
75- return ;
76+ if (_style == _Style .requiredNamed) {
77+ // If there are optional positional parameters, we can't add required
78+ // named parameters.
79+ return ;
80+ } else {
81+ containsOptionalPositional = true ;
82+ }
7683 } else if (parameter.isNamed) {
7784 firstNamedParameter = parameter;
7885 break ;
7986 }
8087 }
8188
82- var fieldsRecords = fields.map (_parameterForField).toList ();
83- var requiredFirst = getCodeStyleOptions (
84- unitResult.file,
85- ).requiredNamedParametersFirst;
86- if (requiredFirst) {
87- fieldsRecords.sort ((a, b) {
88- if (a.isRequired && ! b.isRequired) {
89- return - 1 ;
90- } else if (! a.isRequired && b.isRequired) {
91- return 1 ;
92- }
93- return a.parameter.compareTo (b.parameter);
94- });
95- }
96- var requiredParameters = fieldsRecords.where ((r) => r.isRequired);
97- var optionalParameters = fieldsRecords
98- .where ((r) => ! r.isRequired)
99- .map ((r) => r.parameter);
100- var fieldParametersCode = fieldsRecords.map ((r) => r.parameter).join (', ' );
10189 await builder.addDartFileEdit (file, (builder) {
102- if (firstNamedParameter != null &&
103- requiredFirst &&
104- requiredParameters.isNotEmpty) {
105- builder.addSimpleInsertion (
106- firstNamedParameter.offset,
107- '${requiredParameters .map ((r ) => r .parameter ).join (', ' )}, ' ,
108- );
109- if (optionalParameters.isNotEmpty) {
110- fieldParametersCode = optionalParameters.join (', ' );
90+ var insertOffset =
91+ (lastRequiredPositionalParameter ??
92+ constructor.parameters.leftParenthesis)
93+ .end;
94+ var mappedFields = fields.map (
95+ (field) => (field, isRequired: _isFieldRequired (field)),
96+ );
97+ var initializer = < ({String publicName, FieldElement field})> [];
98+ var addCurlyBraces =
99+ firstNamedParameter == null && _style == _Style .requiredNamed;
100+ var parametersAtCurly = getCodeStyleOptions (
101+ unitResult.file,
102+ ).requiredNamedParametersFirst;
103+ var curlyOpen = lastRequiredPositionalParameter == null ? '{' : ', {' ;
104+ if (addCurlyBraces && ! parametersAtCurly) {
105+ builder.addSimpleInsertion (insertOffset, curlyOpen);
106+ }
107+ if (! addCurlyBraces && _style == _Style .requiredNamed) {
108+ if (parametersAtCurly) {
109+ insertOffset = firstNamedParameter! .offset;
111110 } else {
112- return ; // No optional parameters to add.
111+ insertOffset =
112+ parameters.lastOrNull? .end ??
113+ constructor.parameters.rightParenthesis.offset;
113114 }
114115 }
115- if (_style == _Style .requiredNamed) {
116- var lastParameter = parameters.lastOrNull;
117- if (lastParameter != null ) {
118- var write = ', ' ;
119- if (! lastParameter.isNamed) {
120- write += '{$fieldParametersCode }' ;
116+ if (parametersAtCurly) {
117+ mappedFields.sorted ((r1, r2) {
118+ return r1.isRequired == r2.isRequired ? 0 : (r1.isRequired ? - 1 : 1 );
119+ });
120+ }
121+ var requiredFirst =
122+ firstNamedParameter != null &&
123+ firstNamedParameter.isRequiredNamed &&
124+ parametersAtCurly;
125+ builder.addInsertion (insertOffset, (builder) {
126+ var addComma = _style == _Style .base
127+ ? lastRequiredPositionalParameter != null
128+ : ! parametersAtCurly && ! addCurlyBraces;
129+ for (var (field, : isRequired) in mappedFields) {
130+ // If we have a required named parameter already, don't add
131+ // non-required parameters yet.
132+ if (requiredFirst && ! isRequired) {
133+ continue ;
134+ }
135+ if (addComma) {
136+ builder.write (', ' );
137+ }
138+ addComma = true ;
139+ if (isRequired) {
140+ builder.write ('required ' );
141+ }
142+ if (field.isPrivate) {
143+ var nameIndex = field.displayName.indexOf (_charAfterUnderscore);
144+ var publicName = field.displayName.substring (nameIndex);
145+ if (_startsWithNumber.hasMatch (publicName)) {
146+ // Like we do for closures suggesting p0, p1, etc.
147+ publicName = 'p$publicName ' ;
148+ }
149+ builder.writeType (field.type);
150+ builder.write (' $publicName ' );
151+ initializer.add ((field: field, publicName: publicName));
121152 } else {
122- write+= fieldParametersCode ;
153+ builder. write ( 'this.${ field . name }' ) ;
123154 }
124- builder.addSimpleInsertion (parameters.last.end, write);
125- } else {
126- var offset = constructor.parameters.leftParenthesis.end;
127- builder.addSimpleInsertion (offset, '{$fieldParametersCode }' );
128155 }
129- } else if (lastRequiredParameter != null ) {
130- return builder.addSimpleInsertion (
131- lastRequiredParameter.end,
132- ', $fieldParametersCode ' ,
133- );
134- } else {
135- var offset = constructor.parameters.leftParenthesis.end;
136- if (parameters.isNotEmpty) {
137- fieldParametersCode += ', ' ;
156+ if (_style == _Style .base
157+ ? containsOptionalPositional || firstNamedParameter != null
158+ : firstNamedParameter != null && parametersAtCurly) {
159+ builder.write (', ' );
138160 }
139- builder.addSimpleInsertion (offset, fieldParametersCode);
161+ });
162+ insertOffset =
163+ parameters.lastOrNull? .end ??
164+ constructor.parameters.rightParenthesis.offset;
165+ if (requiredFirst) {
166+ builder.addInsertion (insertOffset, (builder) {
167+ for (var (field, : isRequired) in mappedFields) {
168+ if (isRequired) {
169+ continue ;
170+ }
171+ builder.write (', this.${field .name }' );
172+ }
173+ });
174+ }
175+ if (addCurlyBraces) {
176+ builder.addSimpleInsertion (insertOffset, '}' );
177+ }
178+ 179+ if (initializer.isNotEmpty) {
180+ var colonOffset =
181+ constructor.separator? .end ??
182+ constructor.parameters.rightParenthesis.end;
183+ builder.addInsertion (colonOffset, (builder) {
184+ if (constructor.separator == null ) {
185+ builder.write (' :' );
186+ }
187+ var writeComma = false ;
188+ for (var (: field, : publicName) in initializer) {
189+ if (writeComma) {
190+ builder.write (',' );
191+ }
192+ writeComma = true ;
193+ builder.write (' ${field .name } = $publicName ' );
194+ }
195+ if (constructor.initializers.isNotEmpty) {
196+ builder.write (',' );
197+ }
198+ });
140199 }
141200 });
142201 }
143202
144- _FieldRecord _parameterForField (FieldElement field) {
145- var prefix = '' ;
146- var isRequired = false ;
147- if (typeSystem.isPotentiallyNonNullable (field.type) &&
148- _style == _Style .requiredNamed) {
149- isRequired = true ;
150- prefix = 'required ' ;
151- }
152- return (isRequired: isRequired, parameter: '${prefix }this.${field .name }' );
153- }
203+ bool _isFieldRequired (FieldElement field) =>
204+ _style == _Style .requiredNamed &&
205+ typeSystem.isPotentiallyNonNullable (field.type);
154206}
155207
156208enum _Style { base , requiredNamed }
0 commit comments