[analyzer] Add a "constructor" modifier for LSP semantic tokens

Fixes https://github.com/dart-lang/sdk/issues/45861.

Change-Id: Ib97e14d6e23dfd0c48e1ad8abb18b42a4c2f9cb1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/197401
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/src/lsp/constants.dart b/pkg/analysis_server/lib/src/lsp/constants.dart
index 4afc13e..7f10b84 100644
--- a/pkg/analysis_server/lib/src/lsp/constants.dart
+++ b/pkg/analysis_server/lib/src/lsp/constants.dart
@@ -104,9 +104,13 @@
   /// - parameter
   static const label = SemanticTokenModifiers('label');
 
+  /// A modifier applied to constructors to allow colouring them differently
+  /// to class names that are not constructors.
+  static const constructor = SemanticTokenModifiers('constructor');
+
   /// All custom semantic token modifiers, used to populate the LSP Legend which must
   /// include all used modifiers.
-  static const values = [control, label];
+  static const values = [control, label, constructor];
 }
 
 abstract class CustomSemanticTokenTypes {
diff --git a/pkg/analysis_server/lib/src/lsp/semantic_tokens/mapping.dart b/pkg/analysis_server/lib/src/lsp/semantic_tokens/mapping.dart
index 66e4b26..c83e229 100644
--- a/pkg/analysis_server/lib/src/lsp/semantic_tokens/mapping.dart
+++ b/pkg/analysis_server/lib/src/lsp/semantic_tokens/mapping.dart
@@ -15,6 +15,7 @@
   HighlightRegionType.COMMENT_DOCUMENTATION: {
     SemanticTokenModifiers.documentation
   },
+  HighlightRegionType.CONSTRUCTOR: {CustomSemanticTokenModifiers.constructor},
   HighlightRegionType.DYNAMIC_LOCAL_VARIABLE_DECLARATION: {
     SemanticTokenModifiers.declaration
   },
diff --git a/pkg/analysis_server/test/lsp/semantic_tokens_test.dart b/pkg/analysis_server/test/lsp/semantic_tokens_test.dart
index 6fcc641..20d0bfc 100644
--- a/pkg/analysis_server/test/lsp/semantic_tokens_test.dart
+++ b/pkg/analysis_server/test/lsp/semantic_tokens_test.dart
@@ -82,6 +82,46 @@
     expect(decoded, equals(expected));
   }
 
+  Future<void> test_class_constructors() async {
+    final content = '''
+    class MyClass {
+      MyClass();
+      MyClass.named();
+    }
+
+    final a = MyClass();
+    final b = MyClass.named();
+    ''';
+
+    final expected = [
+      _Token('class', SemanticTokenTypes.keyword),
+      _Token('MyClass', SemanticTokenTypes.class_),
+      _Token('MyClass', SemanticTokenTypes.class_),
+      _Token('MyClass', SemanticTokenTypes.class_),
+      _Token('named', SemanticTokenTypes.class_,
+          [CustomSemanticTokenModifiers.constructor]),
+      _Token('final', SemanticTokenTypes.keyword),
+      _Token('a', SemanticTokenTypes.variable,
+          [SemanticTokenModifiers.declaration]),
+      _Token('MyClass', SemanticTokenTypes.class_,
+          [CustomSemanticTokenModifiers.constructor]),
+      _Token('final', SemanticTokenTypes.keyword),
+      _Token('b', SemanticTokenTypes.variable,
+          [SemanticTokenModifiers.declaration]),
+      _Token('MyClass', SemanticTokenTypes.class_,
+          [CustomSemanticTokenModifiers.constructor]),
+      _Token('named', SemanticTokenTypes.class_,
+          [CustomSemanticTokenModifiers.constructor])
+    ];
+
+    await initialize();
+    await openFile(mainFileUri, withoutMarkers(content));
+
+    final tokens = await getSemanticTokens(mainFileUri);
+    final decoded = decodeSemanticTokens(content, tokens);
+    expect(decoded, equals(expected));
+  }
+
   Future<void> test_class_fields() async {
     final content = '''
     class MyClass {
@@ -119,7 +159,8 @@
       _Token('final', SemanticTokenTypes.keyword),
       _Token('a', SemanticTokenTypes.variable,
           [SemanticTokenModifiers.declaration]),
-      _Token('MyClass', SemanticTokenTypes.class_),
+      _Token('MyClass', SemanticTokenTypes.class_,
+          [CustomSemanticTokenModifiers.constructor]),
       _Token('print', SemanticTokenTypes.function),
       _Token('a', SemanticTokenTypes.variable),
       _Token('myField', SemanticTokenTypes.property),
@@ -197,7 +238,8 @@
       _Token('final', SemanticTokenTypes.keyword),
       _Token('a', SemanticTokenTypes.variable,
           [SemanticTokenModifiers.declaration]),
-      _Token('MyClass', SemanticTokenTypes.class_),
+      _Token('MyClass', SemanticTokenTypes.class_,
+          [CustomSemanticTokenModifiers.constructor]),
       _Token('print', SemanticTokenTypes.function),
       _Token('a', SemanticTokenTypes.variable),
       _Token('myGetter', SemanticTokenTypes.property),
@@ -255,7 +297,8 @@
       _Token('final', SemanticTokenTypes.keyword),
       _Token('a', SemanticTokenTypes.variable,
           [SemanticTokenModifiers.declaration]),
-      _Token('MyClass', SemanticTokenTypes.class_),
+      _Token('MyClass', SemanticTokenTypes.class_,
+          [CustomSemanticTokenModifiers.constructor]),
       _Token('a', SemanticTokenTypes.variable),
       _Token('myMethod', SemanticTokenTypes.method),
       _Token('MyClass', SemanticTokenTypes.class_),
@@ -443,7 +486,8 @@
       _Token('a', SemanticTokenTypes.variable,
           [SemanticTokenModifiers.declaration]),
       _Token('new', SemanticTokenTypes.keyword),
-      _Token('Object', SemanticTokenTypes.class_),
+      _Token('Object', SemanticTokenTypes.class_,
+          [CustomSemanticTokenModifiers.constructor]),
       _Token('await', SemanticTokenTypes.keyword,
           [CustomSemanticTokenModifiers.control]),
       _Token('null', SemanticTokenTypes.keyword),