22// for details. All rights reserved. Use of this source code is governed by a
33// BSD-style license that can be found in the LICENSE file.
44
5- import 'package:_fe_analyzer_shared/src/scanner/token.dart' ;
65import 'package:analysis_server/src/services/completion/dart/utilities.dart' ;
76import 'package:analysis_server/src/services/correction/fix.dart' ;
87import 'package:analysis_server_plugin/edit/dart/correction_producer.dart' ;
98import 'package:analysis_server_plugin/src/correction/fix_generators.dart' ;
109import 'package:analyzer/dart/ast/ast.dart' ;
10+ import 'package:analyzer/dart/ast/token.dart' ;
1111import 'package:analyzer/dart/element/element.dart' ;
1212import 'package:analyzer/diagnostic/diagnostic.dart' ;
1313import 'package:analyzer/error/error.dart' ;
@@ -68,7 +68,7 @@ class AddMissingRequiredArgument extends ResolvedCorrectionProducer {
6868 }
6969
7070 var diagnostic = this .diagnostic;
71- if (diagnostic is ! Diagnostic ) {
71+ if (diagnostic == null ) {
7272 return ;
7373 }
7474
@@ -80,6 +80,7 @@ class AddMissingRequiredArgument extends ResolvedCorrectionProducer {
8080 // Should not happen since the current diagnostic is in the list of errors
8181 // where this fix is valid.
8282 if (diagnostics.isEmpty) {
83+ assert (false , 'No valid diagnostics found, but expected at least one.' );
8384 diagnostics = [diagnostic];
8485 }
8586
@@ -91,92 +92,135 @@ class AddMissingRequiredArgument extends ResolvedCorrectionProducer {
9192
9293 _missingParameters = diagnostics.length;
9394
94- for (var (index, diagnostic) in diagnostics.indexed) {
95- if (targetElement is ExecutableElement && argumentList != null ) {
96- // Format: "Missing required argument 'foo'."
97- var messageParts = diagnostic.problemMessage
98- .messageText (includeUrl: false )
99- .split ("'" );
100- if (messageParts.length < 2 ) {
101- return ;
95+ if (argumentList == null || targetElement is ! ExecutableElement ) {
96+ return ;
97+ }
98+ var arguments = argumentList.arguments;
99+
100+ int offset;
101+ var insertLeadingComma = false ;
102+ var insertFlutterTrailingComma = true ;
103+ var insertBetweenFlutterParams = false ;
104+ if (arguments.isEmpty) {
105+ offset = argumentList.leftParenthesis.end;
106+ } else {
107+ var lastArgument = arguments.last;
108+ offset = lastArgument.end;
109+ insertLeadingComma = true ;
110+ if (creation.isWidgetExpression) {
111+ // If the last argument is a `child:` or `children:` parameter in a
112+ // Flutter widget, insert the new parameter(s) before it.
113+ if (lastArgument is NamedExpression ) {
114+ if (lastArgument.isChildArgument || lastArgument.isChildrenArgument) {
115+ offset = lastArgument.offset;
116+ insertBetweenFlutterParams = true ;
117+ // Insert a leading comma if we are have a case like:
118+ // ```dart
119+ // MyWidget(other: 1, child: ...,)
120+ // ```
121+ // so that we get:
122+ // ```dart
123+ // MyWidget(other: 1, newParam: ..., child: ...,)
124+ // ```
125+ // since the offset is at the end of `other: 1`.
126+ insertLeadingComma = arguments.length > 1 ;
127+ }
102128 }
129+ if (lastArgument.endToken.next case Token (
130+ type: TokenType .COMMA ,
131+ ) when ! insertBetweenFlutterParams) {
132+ // If there is a trailing comma after the last argument, don't add
133+ // another one.
134+ insertFlutterTrailingComma = false ;
135+ }
136+ }
137+ }
103138
104- var parameterName = messageParts[1 ];
139+ Map <String , DefaultArgument ?> parameters = {};
140+ for (var diagnostic in diagnostics) {
141+ // Format: "Missing required argument 'foo'."
142+ var messageParts = diagnostic.problemMessage
143+ .messageText (includeUrl: false )
144+ .split ("'" );
145+ if (messageParts.length < 2 ) {
146+ return ;
147+ }
148+
149+ var parameterName = messageParts[1 ];
105150
106- var missingParameter = targetElement.formalParameters.firstWhereOrNull (
107- (p) => p.name == parameterName,
151+ var missingParameter = targetElement.formalParameters.firstWhereOrNull (
152+ (p) => p.name == parameterName,
153+ );
154+ if (missingParameter == null ) {
155+ assert (
156+ false ,
157+ 'Could not find parameter $parameterName , but expected since it was '
158+ 'reported as missing.' ,
108159 );
109- if (missingParameter == null ) {
110- return ;
111- }
160+ continue ;
161+ }
112162
113- int offset;
114- var hasTrailingComma = false ;
115- var insertBetweenParams = false ;
116- var arguments = argumentList.arguments;
117- if (arguments.isEmpty) {
118- offset = argumentList.leftParenthesis.end;
119- } else {
120- var lastArgument = arguments.last;
121- offset = lastArgument.end;
122- hasTrailingComma =
123- lastArgument.endToken.next! .type == TokenType .COMMA ;
124-
125- if (lastArgument is NamedExpression && creation.isWidgetExpression) {
126- if (lastArgument.isChildArgument ||
127- lastArgument.isChildrenArgument) {
128- offset = lastArgument.offset;
129- hasTrailingComma = true ;
130- insertBetweenParams = true ;
131- }
132- }
133- }
163+ var codeStyleOptions = getCodeStyleOptions (unitResult.file);
164+ var defaultValue = getDefaultStringParameterValue (
165+ missingParameter,
166+ codeStyleOptions.preferredQuoteForStrings,
167+ );
134168
135- var codeStyleOptions = getCodeStyleOptions (unitResult.file);
136- var defaultValue = getDefaultStringParameterValue (
137- missingParameter,
138- codeStyleOptions.preferredQuoteForStrings,
139- );
169+ parameters[parameterName] = defaultValue;
170+ }
140171
141- await builder.addDartFileEdit (file, (builder) {
142- builder.addInsertion (offset, (builder) {
143- if ((arguments.isNotEmpty || index > 0 ) && ! insertBetweenParams) {
144- builder.write (', ' );
145- }
172+ var parametersLength = parameters.length;
146173
147- builder.write ('$parameterName : ' );
148-
149- // Use defaultValue.cursorPosition if it's not null.
150- if (defaultValue != null ) {
151- var text = defaultValue.text;
152- var cursorPosition = defaultValue.cursorPosition;
153- if (cursorPosition != null ) {
154- builder.write (text.substring (0 , cursorPosition));
155- builder.selectHere ();
156- builder.write (text.substring (cursorPosition));
157- } else {
158- builder.addSimpleLinkedEdit ('VALUE' , text);
159- }
174+ await builder.addDartFileEdit (file, (builder) {
175+ builder.addInsertion (offset, (builder) {
176+ if (insertLeadingComma) {
177+ builder.write (', ' );
178+ }
179+ var entries = parameters.entries.toList ();
180+ var childOrChildren = entries.firstWhereOrNull (
181+ (e) => const {'child' , 'children' }.contains (e.key),
182+ );
183+ if (childOrChildren != null ) {
184+ // If there is a child: or children: parameter, insert it last.
185+ entries.remove (childOrChildren);
186+ entries.add (childOrChildren);
187+ }
188+ entries.forEachIndexed ((index, entry) {
189+ var MapEntry (key: parameterName, value: defaultValue) = entry;
190+
191+ builder.write ('$parameterName : ' );
192+
193+ // Use defaultValue.cursorPosition if it's not null.
194+ if (defaultValue != null ) {
195+ var text = defaultValue.text;
196+ var cursorPosition = defaultValue.cursorPosition;
197+ if (cursorPosition != null ) {
198+ builder.write (text.substring (0 , cursorPosition));
199+ builder.selectHere ();
200+ builder.write (text.substring (cursorPosition));
160201 } else {
161- builder.addSimpleLinkedEdit ('VALUE' , 'null' );
202+ builder.addSimpleLinkedEdit ('VALUE' , text );
162203 }
204+ } else {
205+ builder.addSimpleLinkedEdit ('VALUE' , 'null' );
206+ }
163207
164- if (creation.isWidgetExpression ) {
165- // Insert a trailing comma after Flutter instance creation params.
166- if (! hasTrailingComma ) {
167- builder. write ( ',' );
168- } else if (insertBetweenParams) {
169- builder. writeln ( ',' );
170-
171- // Insert indent before the child: or children: param.
172- var indent = utils. getLinePrefix (offset);
173- builder. write (indent);
174- }
175- }
176- });
208+ if (index < parametersLength - 1 ) {
209+ builder. write ( ', ' );
210+ } else if (insertBetweenFlutterParams ) {
211+ // Insert a trailing comma after Flutter instance creation params.
212+ builder. writeln ( ',' );
213+ // Insert indent before the child: or children: param.
214+ var indent = utils. getLinePrefix (offset);
215+ builder. write (indent);
216+ } else if (creation.isWidgetExpression &&
217+ insertFlutterTrailingComma) {
218+ // If this is a Flutter widget, format with a trailing comma.
219+ builder. write ( ',' );
220+ }
177221 });
178- }
179- }
222+ });
223+ });
180224 }
181225}
182226
0 commit comments