Version 2.16.0-12.0.dev

Merge commit '39d930305c40b3a33ef73360d2232e3be7dcaba8' into 'dev'
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_document_color_presentation.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_document_color_presentation.dart
index 52f09fb..06fdd44 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_document_color_presentation.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_document_color_presentation.dart
@@ -9,6 +9,7 @@
 import 'package:analysis_server/src/lsp/mapping.dart';
 import 'package:analysis_server/src/utilities/flutter.dart';
 import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/source/source_range.dart';
 import 'package:analyzer/src/dart/analysis/session_helper.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
@@ -49,24 +50,68 @@
     return (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
   }
 
-  /// Builds a list of valid color presentations for the requested color.
+  /// Creates a [ColorPresentation] for inserting code to produce a dart:ui
+  /// or Flutter Color at [editRange].
   ///
-  /// Currently only a single presentation (`Color(0xAARRGGBB)`) is provided.
+  /// [colorType] is the Type of the Color class whose constructor will be
+  /// called. This will be replaced into [editRange] and any required import
+  /// statement will produce additional edits.
+  ///
+  /// [label] is the visible label shown to the user and should roughly reflect
+  /// the code that will be inserted.
+  ///
+  /// [invocationString] is written immediately after [colorType] in [editRange].
+  Future<ColorPresentation> _createColorPresentation(
+    ResolvedUnitResult unit,
+    SourceRange editRange,
+    ClassElement colorType,
+    String label,
+    String invocationString,
+  ) async {
+    final builder = ChangeBuilder(session: unit.session);
+    await builder.addDartFileEdit(unit.path, (builder) {
+      builder.addReplacement(editRange, (builder) {
+        builder.writeType(colorType.thisType);
+        builder.write(invocationString);
+      });
+    });
+
+    // We can only apply changes to the same file, so filter any change from the
+    // builder to only include this file, otherwise we may corrupt the users
+    // source (although hopefully we don't produce edits for other files).
+    final editsForThisFile = builder.sourceChange.edits
+        .where((edit) => edit.file == unit.path)
+        .expand((edit) => edit.edits)
+        .toList();
+
+    // LSP requires that we separate the main edit (changing the color code)
+    // from anything else (imports).
+    final mainEdit =
+        editsForThisFile.singleWhere((edit) => edit.offset == editRange.offset);
+    final otherEdits =
+        editsForThisFile.where((edit) => edit.offset != editRange.offset);
+
+    return ColorPresentation(
+      label: label,
+      textEdit: toTextEdit(unit.lineInfo, mainEdit),
+      additionalTextEdits: otherEdits.isNotEmpty
+          ? otherEdits.map((edit) => toTextEdit(unit.lineInfo, edit)).toList()
+          : null,
+    );
+  }
+
+  /// Builds a list of valid color presentations for the requested color.
   Future<ErrorOr<List<ColorPresentation>>> _getPresentations(
     ColorPresentationParams params,
     ResolvedUnitResult unit,
   ) async {
     // The values in LSP are decimals 0-1 so should be scaled up to 255 that
-    // we use internally.
-    final colorValue = _colorValueForComponents(
-      (params.color.alpha * 255).toInt(),
-      (params.color.red * 255).toInt(),
-      (params.color.green * 255).toInt(),
-      (params.color.blue * 255).toInt(),
-    );
-
-    final colorValueHex =
-        '0x${colorValue.toRadixString(16).toUpperCase().padLeft(8, '0')}';
+    // we use internally (except for opacity is which 0-1).
+    final alpha = (params.color.alpha * 255).toInt();
+    final red = (params.color.red * 255).toInt();
+    final green = (params.color.green * 255).toInt();
+    final blue = (params.color.blue * 255).toInt();
+    final opacity = params.color.alpha;
 
     final editStart = toOffset(unit.lineInfo, params.range.start);
     final editEnd = toOffset(unit.lineInfo, params.range.end);
@@ -87,36 +132,38 @@
       return success([]);
     }
 
-    final builder = ChangeBuilder(session: unit.session);
-    await builder.addDartFileEdit(unit.path, (builder) {
-      builder.addReplacement(editRange, (builder) {
-        builder.writeType(colorType.thisType);
-        builder.write('($colorValueHex)');
-      });
-    });
+    final colorValue = _colorValueForComponents(alpha, red, green, blue);
+    final colorValueHex =
+        '0x${colorValue.toRadixString(16).toUpperCase().padLeft(8, '0')}';
 
-    // We can only apply changes to the same file, so filter any change from the
-    // builder to only include this file, otherwise we may corrupt the users
-    // source (although hopefully we don't produce edits for other files).
-    final editsForThisFile = builder.sourceChange.edits
-        .where((edit) => edit.file == unit.path)
-        .expand((edit) => edit.edits)
-        .toList();
+    final colorFromARGB = await _createColorPresentation(
+      unit,
+      editRange,
+      colorType,
+      'Color.fromARGB($alpha, $red, $green, $blue)',
+      '.fromARGB($alpha, $red, $green, $blue)',
+    );
 
-    // LSP requires that we separate the main edit (changing the color code)
-    // from anything else (imports).
-    final mainEdit =
-        editsForThisFile.singleWhere((edit) => edit.offset == editRange.offset);
-    final otherEdits =
-        editsForThisFile.where((edit) => edit.offset != editRange.offset);
+    final colorFromRGBO = await _createColorPresentation(
+      unit,
+      editRange,
+      colorType,
+      'Color.fromRGBO($red, $green, $blue, $opacity)',
+      '.fromRGBO($red, $green, $blue, $opacity)',
+    );
+
+    final colorDefault = await _createColorPresentation(
+      unit,
+      editRange,
+      colorType,
+      'Color($colorValueHex)',
+      '($colorValueHex)',
+    );
 
     return success([
-      ColorPresentation(
-        label: 'Color($colorValueHex)',
-        textEdit: toTextEdit(unit.lineInfo, mainEdit),
-        additionalTextEdits:
-            otherEdits.map((edit) => toTextEdit(unit.lineInfo, edit)).toList(),
-      ),
+      colorFromARGB,
+      colorFromRGBO,
+      colorDefault,
     ]);
   }
 }
diff --git a/pkg/analysis_server/test/lsp/document_color_test.dart b/pkg/analysis_server/test/lsp/document_color_test.dart
index 12dd16d..55dbf74 100644
--- a/pkg/analysis_server/test/lsp/document_color_test.dart
+++ b/pkg/analysis_server/test/lsp/document_color_test.dart
@@ -17,6 +17,9 @@
 
 @reflectiveTest
 class DocumentColorPresentationTest extends AbstractLspAnalysisServerTest {
+  late Range colorRange, importRange;
+  final uiImportUri = 'package:ui/ui.dart';
+
   @override
   void setUp() {
     super.setUp();
@@ -30,45 +33,30 @@
     // calling colorPresentation directly to get the new code (and not fetching
     // colors already in the file).
     const content = '''
-const white = [[]];
+[[]]const white = [[]];
 ''';
 
-    const expectedMainEdit = '''
-const white = Color(0xFFFFFFFF);
-''';
-
-    const expectedAllEdits = '''
-import 'package:ui/ui.dart';
-
-const white = Color(0xFFFFFFFF);
-''';
+    final ranges = rangesFromMarkers(content);
+    importRange = ranges[0];
+    colorRange = ranges[1];
 
     newFile(mainFilePath, content: withoutMarkers(content));
     await initialize();
 
     final colorPresentations = await getColorPresentation(
       mainFileUri.toString(),
-      rangeFromMarkers(content),
+      colorRange,
       Color(alpha: 1, red: 1, green: 1, blue: 1),
     );
-    expect(colorPresentations, hasLength(1));
 
-    final colorPresentation = colorPresentations[0];
-    expect(colorPresentation.label, 'Color(0xFFFFFFFF)');
-
-    // Test the main edit just replaces the color.
-    final contentWithMainEdit = applyTextEdits(
-      withoutMarkers(content),
-      [colorPresentation.textEdit!],
+    expect(
+      colorPresentations,
+      equals([
+        _color('Color.fromARGB(255, 255, 255, 255)', importUri: uiImportUri),
+        _color('Color.fromRGBO(255, 255, 255, 1)', importUri: uiImportUri),
+        _color('Color(0xFFFFFFFF)', importUri: uiImportUri),
+      ]),
     );
-    expect(contentWithMainEdit, equals(expectedMainEdit));
-
-    // Test all of the edits also adds the import.
-    final contentWithAllEdits = applyTextEdits(
-      withoutMarkers(content),
-      [colorPresentation.textEdit!, ...colorPresentation.additionalTextEdits!],
-    );
-    expect(contentWithAllEdits, equals(expectedAllEdits));
   }
 
   Future<void> test_nonDartFile() async {
@@ -89,21 +77,46 @@
 
     const white = [[Color(0xFFFFFFFF)]];
     ''';
+    colorRange = rangeFromMarkers(content);
+
     newFile(mainFilePath, content: withoutMarkers(content));
     await initialize();
 
     final colorPresentations = await getColorPresentation(
       mainFileUri.toString(),
-      rangeFromMarkers(content),
+      colorRange,
       // Send a different color to what's in the source to simulate the user
       // having changed in the color picker. This is the one that we should be
       // creating a presentation for, not the one in the source.
       Color(alpha: 1, red: 1, green: 0, blue: 0),
     );
-    expect(colorPresentations, hasLength(1));
 
-    final colorPresentation = colorPresentations[0];
-    expect(colorPresentation.label, 'Color(0xFFFF0000)');
+    expect(
+      colorPresentations,
+      equals([
+        _color('Color.fromARGB(255, 255, 0, 0)'),
+        _color('Color.fromRGBO(255, 0, 0, 1)'),
+        _color('Color(0xFFFF0000)'),
+      ]),
+    );
+  }
+
+  /// Creates a [ColorPresentation] for comparing against actual results.
+  ColorPresentation _color(
+    String label, {
+    String? colorCode,
+    String? importUri,
+  }) {
+    final edit = TextEdit(range: colorRange, newText: colorCode ?? label);
+    final additionalEdits = importUri != null
+        ? [TextEdit(range: importRange, newText: "import '$importUri';\n\n")]
+        : null;
+
+    return ColorPresentation(
+      label: label,
+      textEdit: edit,
+      additionalTextEdits: additionalEdits,
+    );
   }
 }
 
diff --git a/pkg/dart_internal/CHANGELOG.md b/pkg/dart_internal/CHANGELOG.md
index c49fe05..8894fed 100644
--- a/pkg/dart_internal/CHANGELOG.md
+++ b/pkg/dart_internal/CHANGELOG.md
@@ -1,3 +1,11 @@
+## 0.2.3
+
+- Support the latest Dart SDK.
+
+## 0.2.2
+
+- Support the latest Dart SDK.
+
 ## 0.2.1
 
 - Support the latest Dart SDK.
diff --git a/tools/VERSION b/tools/VERSION
index 6a1784b..72690ae 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 16
 PATCH 0
-PRERELEASE 11
+PRERELEASE 12
 PRERELEASE_PATCH 0
\ No newline at end of file