[Code completion] Move logic from SuggestionBuilder to NamedArgumentSuggestion.

Change-Id: I1e6d1a75ecb37c8dd7ee65414d880431347053a2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/384302
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Keerti Parthasarathy <keertip@google.com>
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/candidate_suggestion.dart b/pkg/analysis_server/lib/src/services/completion/dart/candidate_suggestion.dart
index 83cc29a..a2108a2 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/candidate_suggestion.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/candidate_suggestion.dart
@@ -325,7 +325,7 @@
       required this.referencingInterface});
 
   @override
-  String get completion => element.name;
+  String get completion => element.displayName;
 }
 
 /// The information about a candidate suggestion based on a formal parameter.
@@ -661,16 +661,73 @@
   /// doesn't need to be overridden.
   final int? replacementLength;
 
-  NamedArgumentSuggestion(
-      {required this.parameter,
-      required this.appendColon,
-      required this.appendComma,
-      this.replacementLength,
-      required super.matcherScore});
+  final bool isWidget;
+
+  String preferredQuoteForStrings;
+
+  late String _completion;
+
+  // Indicates whether _completion and _selectionOffset have been initialized.
+  bool _initialized = false;
+
+  late int _selectionOffset;
+
+  NamedArgumentSuggestion({
+    required this.parameter,
+    required this.appendColon,
+    required this.appendComma,
+    this.replacementLength,
+    required super.matcherScore,
+    required this.preferredQuoteForStrings,
+    this.isWidget = false,
+  });
 
   @override
-  String get completion =>
-      '${parameter.name}${appendColon ? ': ' : ''}${appendComma ? ',' : ''}';
+  String get completion {
+    _init();
+    return _completion;
+  }
+
+  /// The offset, from the beginning of the inserted text, where the cursor
+  /// should be positioned.
+  int get selectionOffset {
+    _init();
+    return _selectionOffset;
+  }
+
+  void _init() {
+    if (_initialized) {
+      return;
+    }
+    var completion = parameter.name;
+    if (appendColon) {
+      completion += ': ';
+    }
+    var selectionOffset = completion.length;
+
+    // Optionally add Flutter child widget details.
+    // TODO(pq): revisit this special casing; likely it can be generalized away.
+    if (isWidget && appendColon) {
+      var defaultValue =
+          getDefaultStringParameterValue(parameter, preferredQuoteForStrings);
+      // TODO(devoncarew): Should we remove the check here? We would then
+      // suggest values for param types like closures.
+      if (defaultValue != null && defaultValue.text == '[]') {
+        var completionLength = completion.length;
+        completion += defaultValue.text;
+        var cursorPosition = defaultValue.cursorPosition;
+        if (cursorPosition != null) {
+          selectionOffset = completionLength + cursorPosition;
+        }
+      }
+    }
+    if (appendComma) {
+      completion += ',';
+    }
+    _completion = completion;
+    _selectionOffset = selectionOffset;
+    _initialized = true;
+  }
 }
 
 /// The information about a candidate suggestion based on a getter or setter.
@@ -1163,6 +1220,8 @@
             appendColon: suggestion.appendColon,
             appendComma: suggestion.appendComma,
             replacementLength: suggestion.replacementLength,
+            completion: suggestion.completion,
+            selectionOffset: suggestion.selectionOffset,
             relevance: relevance);
       case NameSuggestion():
         suggestName(suggestion.name);
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/completion_state.dart b/pkg/analysis_server/lib/src/services/completion/dart/completion_state.dart
index 11e8d7d..c39a66a 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/completion_state.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/completion_state.dart
@@ -5,6 +5,7 @@
 import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
 import 'package:analysis_server/src/services/completion/dart/utilities.dart';
 import 'package:analysis_server_plugin/src/utilities/selection.dart';
+import 'package:analyzer/dart/analysis/code_style_options.dart';
 import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
@@ -59,6 +60,10 @@
   /// The element of the library containing the completion location.
   LibraryElement get libraryElement => request.libraryElement;
 
+  /// The type of quotes preferred for [String]s as specified in [CodeStyleOptions].
+  String get preferredQuoteForStrings => request
+      .fileState.analysisOptions.codeStyleOptions.preferredQuoteForStrings;
+
   /// The type of `this` at the completion location, or `null` if the completion
   /// location doesn't allow `this` to be used.
   DartType? get thisType {
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/in_scope_completion_pass.dart b/pkg/analysis_server/lib/src/services/completion/dart/in_scope_completion_pass.dart
index 214da2c..fbd72ff 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/in_scope_completion_pass.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/in_scope_completion_pass.dart
@@ -12,6 +12,7 @@
 import 'package:analysis_server/src/services/completion/dart/override_helper.dart';
 import 'package:analysis_server/src/services/completion/dart/suggestion_collector.dart';
 import 'package:analysis_server/src/services/completion/dart/uri_helper.dart';
+import 'package:analysis_server/src/services/completion/dart/utilities.dart';
 import 'package:analysis_server/src/services/completion/dart/visibility_tracker.dart';
 import 'package:analysis_server/src/utilities/extensions/ast.dart';
 import 'package:analysis_server/src/utilities/extensions/completion_request.dart';
@@ -368,12 +369,15 @@
         for (var parameter in availableNamedParameters) {
           var matcherScore = state.matcher.score(parameter.displayName);
           if (matcherScore != -1) {
+            var isWidget = isFlutterWidgetParameter(parameter);
             collector.addSuggestion(NamedArgumentSuggestion(
               parameter: parameter,
               appendColon: true,
               appendComma: appendComma,
               replacementLength: replacementLength,
               matcherScore: matcherScore,
+              preferredQuoteForStrings: state.preferredQuoteForStrings,
+              isWidget: isWidget,
             ));
           }
         }
@@ -1972,11 +1976,15 @@
                 if (!usedNames.contains(parameter.name)) {
                   var matcherScore = state.matcher.score(parameter.displayName);
                   if (matcherScore != -1) {
+                    var isWidget = isFlutterWidgetParameter(parameter);
                     collector.addSuggestion(NamedArgumentSuggestion(
                         parameter: parameter,
                         matcherScore: matcherScore,
                         appendColon: appendColon,
-                        appendComma: false));
+                        appendComma: false,
+                        isWidget: isWidget,
+                        preferredQuoteForStrings:
+                            state.preferredQuoteForStrings));
                   }
                 }
               }
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart b/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
index 9923365..f923c6f 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
@@ -14,7 +14,6 @@
 import 'package:analysis_server/src/services/completion/dart/utilities.dart';
 import 'package:analysis_server/src/utilities/extensions/ast.dart';
 import 'package:analysis_server/src/utilities/extensions/element.dart';
-import 'package:analysis_server/src/utilities/extensions/flutter.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/nullability_suffix.dart';
@@ -581,52 +580,13 @@
   void suggestNamedArgument(ParameterElement parameter,
       {required bool appendColon,
       required bool appendComma,
+      required int selectionOffset,
+      required String completion,
       int? replacementLength,
-      int? relevance}) {
+      required int relevance}) {
     var name = parameter.name;
     var type = parameter.type.getDisplayString();
 
-    var completion = name;
-    if (appendColon) {
-      completion += ': ';
-    }
-    var selectionOffset = completion.length;
-
-    // Optionally add Flutter child widget details.
-    // TODO(pq): revisit this special casing; likely it can be generalized away.
-    var element = parameter.enclosingElement3;
-    // If `appendColon` is false, default values should never be appended.
-    if (element is ConstructorElement && appendColon) {
-      if (element.enclosingElement3.augmented.declaration.isWidget) {
-        var analysisOptions = request.analysisSession.analysisContext
-            .getAnalysisOptionsForFile(
-                request.resourceProvider.getFile(request.path));
-        var codeStyleOptions = analysisOptions.codeStyleOptions;
-        // Don't bother with nullability. It won't affect default list values.
-        var defaultValue =
-            getDefaultStringParameterValue(parameter, codeStyleOptions);
-        // TODO(devoncarew): Should we remove the check here? We would then
-        // suggest values for param types like closures.
-        if (defaultValue != null && defaultValue.text == '[]') {
-          var completionLength = completion.length;
-          completion += defaultValue.text;
-          var cursorPosition = defaultValue.cursorPosition;
-          if (cursorPosition != null) {
-            selectionOffset = completionLength + cursorPosition;
-          }
-        }
-      }
-    }
-
-    if (appendComma) {
-      completion += ',';
-    }
-
-    relevance ??= Relevance.namedArgument;
-    if (parameter.isRequiredNamed || parameter.hasRequired) {
-      relevance = Relevance.requiredNamedArgument;
-    }
-
     var suggestion = DartCompletionSuggestion(
         CompletionSuggestionKind.NAMED_ARGUMENT,
         relevance,
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/utilities.dart b/pkg/analysis_server/lib/src/services/completion/dart/utilities.dart
index 6deb00c..a97c531 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/utilities.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/utilities.dart
@@ -9,7 +9,7 @@
 import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
 import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
 import 'package:analysis_server/src/utilities/extensions/ast.dart';
-import 'package:analyzer/dart/analysis/code_style_options.dart';
+import 'package:analysis_server/src/utilities/extensions/flutter.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/nullability_suffix.dart';
@@ -150,7 +150,7 @@
 
 /// Return a default argument value for the given [parameter].
 DefaultArgument? getDefaultStringParameterValue(
-    ParameterElement parameter, CodeStyleOptions codeStyleOptions) {
+    ParameterElement parameter, String quote) {
   var type = parameter.type;
   if (type is InterfaceType) {
     if (type.isDartCoreList) {
@@ -158,7 +158,6 @@
     } else if (type.isDartCoreMap) {
       return DefaultArgument('{}', cursorPosition: 1);
     } else if (type.isDartCoreString) {
-      var quote = codeStyleOptions.preferredQuoteForStrings;
       return DefaultArgument('$quote$quote', cursorPosition: 1);
     }
   } else if (type is FunctionType) {
@@ -210,6 +209,17 @@
   );
 }
 
+/// Returns true if the [parameter] is part of a constructor for a Flutter
+/// [Widget].
+bool isFlutterWidgetParameter(ParameterElement parameter) {
+  var element = parameter.enclosingElement3;
+  if (element is ConstructorElement &&
+      element.enclosingElement3.augmented.declaration.isWidget) {
+    return true;
+  }
+  return false;
+}
+
 /// Return name of the type of the given [identifier], or, if it unresolved, the
 /// name of its declared [declaredType].
 String? nameForType(SimpleIdentifier identifier, TypeAnnotation? declaredType) {
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_missing_required_argument.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_missing_required_argument.dart
index d229d50..d6e3c80 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/add_missing_required_argument.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/add_missing_required_argument.dart
@@ -94,8 +94,8 @@
       }
 
       var codeStyleOptions = getCodeStyleOptions(unitResult.file);
-      var defaultValue =
-          getDefaultStringParameterValue(missingParameter, codeStyleOptions);
+      var defaultValue = getDefaultStringParameterValue(
+          missingParameter, codeStyleOptions.preferredQuoteForStrings);
 
       await builder.addDartFileEdit(file, (builder) {
         builder.addInsertion(offset, (builder) {