// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:async';

import 'package:analysis_server/lsp_protocol/protocol.dart';
import 'package:analysis_server/src/legacy_analysis_server.dart';
import 'package:analysis_server/src/lsp/constants.dart';
import 'package:analysis_server/src/lsp/semantic_tokens/legend.dart';
import 'package:analysis_server/src/protocol/protocol_internal.dart';
import 'package:analyzer/src/test_utilities/platform.dart';
import 'package:analyzer/src/test_utilities/test_code_format.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart' as plugin;
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
import 'package:analyzer_testing/experiments/experiments.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';

import '../utils/test_code_extensions.dart';
import 'server_abstract.dart';

void main() {
  defineReflectiveSuite(() {
    defineReflectiveTests(SemanticTokensTest);
  });
}

@reflectiveTest
class SemanticTokensTest extends AbstractLspAnalysisServerTest {
  @override
  AnalysisServerOptions get serverOptions =>
      AnalysisServerOptions()..enabledExperiments = experimentsForTests;

  Future<void> test_annotation() async {
    var content = '''
import 'other_file.dart' as other;

@a
@A()
@A.n()
@B(A())
@other.C()
@other.C.n()
void foo() {}

class A {
  const A();
  const A.n();
}

const a = A();

class B {
  final A a;
  const B(this.a);
}
''';

    var otherContent = '''
class C {
  const C();
  const C.n();
}
''';

    var code = TestCode.parseNormalized(content);
    var otherCode = TestCode.parseNormalized(otherContent);

    var expectedStart = [
      _Token('import', SemanticTokenTypes.keyword),
      _Token("'other_file.dart'", SemanticTokenTypes.string),
      _Token('as', SemanticTokenTypes.keyword),
      _Token('other', SemanticTokenTypes.variable, [
        CustomSemanticTokenModifiers.importPrefix,
      ]),
      _Token('@', CustomSemanticTokenTypes.annotation),
      _Token('a', SemanticTokenTypes.property, [
        CustomSemanticTokenModifiers.annotation,
      ]),
      _Token('@', CustomSemanticTokenTypes.annotation),
      _Token('A', SemanticTokenTypes.class_, [
        CustomSemanticTokenModifiers.annotation,
      ]),
      _Token('(', CustomSemanticTokenTypes.annotation),
      _Token(')', CustomSemanticTokenTypes.annotation),
      _Token('@', CustomSemanticTokenTypes.annotation),
      _Token('A', SemanticTokenTypes.class_, [
        CustomSemanticTokenModifiers.annotation,
      ]),
      _Token('.', CustomSemanticTokenTypes.annotation),
      _Token('n', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.constructor,
        CustomSemanticTokenModifiers.annotation,
      ]),
      _Token('(', CustomSemanticTokenTypes.annotation),
      _Token(')', CustomSemanticTokenTypes.annotation),
      _Token('@', CustomSemanticTokenTypes.annotation),
      _Token('B', SemanticTokenTypes.class_, [
        CustomSemanticTokenModifiers.annotation,
      ]),
      _Token('(', CustomSemanticTokenTypes.annotation),
      _Token('A', SemanticTokenTypes.class_, [
        CustomSemanticTokenModifiers.constructor,
      ]),
      _Token(')', CustomSemanticTokenTypes.annotation),
      _Token('@', CustomSemanticTokenTypes.annotation),
      _Token('other', SemanticTokenTypes.variable, [
        CustomSemanticTokenModifiers.importPrefix,
      ]),
      _Token('.', CustomSemanticTokenTypes.annotation),
      _Token('C', SemanticTokenTypes.class_, [
        CustomSemanticTokenModifiers.annotation,
      ]),
      _Token('(', CustomSemanticTokenTypes.annotation),
      _Token(')', CustomSemanticTokenTypes.annotation),
      _Token('@', CustomSemanticTokenTypes.annotation),
      _Token('other', SemanticTokenTypes.variable, [
        CustomSemanticTokenModifiers.importPrefix,
      ]),
      _Token('.', CustomSemanticTokenTypes.annotation),
      _Token('C', SemanticTokenTypes.class_, [
        CustomSemanticTokenModifiers.annotation,
      ]),
      _Token('.', CustomSemanticTokenTypes.annotation),
      _Token('n', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.constructor,
        CustomSemanticTokenModifiers.annotation,
      ]),
      _Token('(', CustomSemanticTokenTypes.annotation),
      _Token(')', CustomSemanticTokenTypes.annotation),
      _Token('void', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.void_,
      ]),
      _Token('foo', SemanticTokenTypes.function, [
        SemanticTokenModifiers.declaration,
        SemanticTokenModifiers.static,
      ]),
    ];

    var otherFilePath = join(projectFolderPath, 'lib', 'other_file.dart');

    newFile(mainFilePath, code.code);
    newFile(otherFilePath, otherCode.code);
    await initialize();

    var tokens = await getSemanticTokens(mainFileUri);
    var decoded = _decodeSemanticTokens(code.code, tokens);
    expect(
      // Only check the first expectedStart.length items since the test code
      // is mostly unrelated to the annotations.
      decoded.sublist(0, expectedStart.length),
      equals(expectedStart),
    );
  }

  Future<void> test_annotation_parameter() async {
    var content = '''
class MyAnnotation {
  const MyAnnotation();
}

class A {
  A([!@MyAnnotation() String a!]);
}
''';

    var expected = [
      _Token('@', CustomSemanticTokenTypes.annotation),
      _Token('MyAnnotation', SemanticTokenTypes.class_, [
        CustomSemanticTokenModifiers.annotation,
      ]),
      _Token('(', CustomSemanticTokenTypes.annotation),
      _Token(')', CustomSemanticTokenTypes.annotation),
      _Token('String', SemanticTokenTypes.class_),
      _Token('a', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_annotation_parameter_super() async {
    var content = '''
class MyAnnotation {
  const MyAnnotation();
}

class A {
  A(String a);
}

class B extends A {
  B([!@MyAnnotation() super.a!]);
}
''';

    var expected = [
      _Token('@', CustomSemanticTokenTypes.annotation),
      _Token('MyAnnotation', SemanticTokenTypes.class_, [
        CustomSemanticTokenModifiers.annotation,
      ]),
      _Token('(', CustomSemanticTokenTypes.annotation),
      _Token(')', CustomSemanticTokenTypes.annotation),
      _Token('super', SemanticTokenTypes.keyword),
      _Token('a', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_annotation_parameter_super_fieldFormal() async {
    var content = '''
class MyAnnotation {
  const MyAnnotation();
}

class A {
  String a;
  A(this.a);
}

class B extends A {
  B([!@MyAnnotation() super.a!]);
}
''';

    var expected = [
      _Token('@', CustomSemanticTokenTypes.annotation),
      _Token('MyAnnotation', SemanticTokenTypes.class_, [
        CustomSemanticTokenModifiers.annotation,
      ]),
      _Token('(', CustomSemanticTokenTypes.annotation),
      _Token(')', CustomSemanticTokenTypes.annotation),
      _Token('super', SemanticTokenTypes.keyword),
      _Token('a', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_annotation_parameter_this() async {
    var content = '''
class MyAnnotation {
  const MyAnnotation();
}

class A {
  String a;
  A([!@MyAnnotation() this.a!]);
}
''';

    var expected = [
      _Token('@', CustomSemanticTokenTypes.annotation),
      _Token('MyAnnotation', SemanticTokenTypes.class_, [
        CustomSemanticTokenModifiers.annotation,
      ]),
      _Token('(', CustomSemanticTokenTypes.annotation),
      _Token(')', CustomSemanticTokenTypes.annotation),
      _Token('this', SemanticTokenTypes.keyword),
      _Token('a', SemanticTokenTypes.variable, [
        CustomSemanticTokenModifiers.instance,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_augmentations() async {
    var mainContent = '''
part 'main_augmentation.dart';

class A {
  void f() {}
  String get g;
}
''';

    var augmentationContent = '''
part of 'main.dart';

augment class A {
  augment void f();
  augment get g => 'augmented';
}
''';

    newFile(mainFilePath, mainContent);
    newFile(mainFileAugmentationPath, augmentationContent);
    await initialize();

    // Main library.
    await _verifyTokens(mainFileUri, mainContent, [
      _Token('part', SemanticTokenTypes.keyword),
      _Token("'main_augmentation.dart'", SemanticTokenTypes.string),
      _Token('class', SemanticTokenTypes.keyword),
      _Token('A', SemanticTokenTypes.class_, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('void', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.void_,
      ]),
      _Token('f', SemanticTokenTypes.method, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token('String', SemanticTokenTypes.class_),
      _Token('get', SemanticTokenTypes.keyword),
      _Token('g', SemanticTokenTypes.property, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.instance,
      ]),
    ]);

    // Augmentation.
    await _verifyTokens(mainFileAugmentationUri, augmentationContent, [
      _Token('part of', SemanticTokenTypes.keyword),
      _Token("'main.dart'", SemanticTokenTypes.string),
      _Token('augment', SemanticTokenTypes.keyword),
      _Token('class', SemanticTokenTypes.keyword),
      _Token('A', SemanticTokenTypes.class_, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('augment', SemanticTokenTypes.keyword),
      _Token('void', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.void_,
      ]),
      _Token('f', SemanticTokenTypes.method, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token('augment', SemanticTokenTypes.keyword),
      _Token('get', SemanticTokenTypes.keyword),
      _Token('g', SemanticTokenTypes.property, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token("'augmented'", SemanticTokenTypes.string),
    ]);
  }

  Future<void> test_class() async {
    var content = '''
/// class docs
class MyClass<T> {
  // class comment
}

// Trailing comment
''';

    var expected = [
      _Token('/// class docs', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token('class', SemanticTokenTypes.keyword),
      _Token('MyClass', SemanticTokenTypes.class_, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('T', SemanticTokenTypes.typeParameter),
      _Token('// class comment', SemanticTokenTypes.comment),
      _Token('// Trailing comment', SemanticTokenTypes.comment),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_class_constructor_primary_declaration() async {
    var content = r'''
class A { const A(); }
mixin M {}

[!
class const B(final int y) {}
class C<T extends Object>.named(
  var int x, [
  final int y = 0,
]) extends A with M implements B {
  this {}
}
!]
''';

    var expected = [
      _Token('class', SemanticTokenTypes.keyword),
      _Token('const', SemanticTokenTypes.keyword),
      _Token('B', SemanticTokenTypes.class_, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('final', SemanticTokenTypes.keyword),
      _Token('int', SemanticTokenTypes.class_),
      _Token('y', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('class', SemanticTokenTypes.keyword),
      _Token('C', SemanticTokenTypes.class_, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('T', SemanticTokenTypes.typeParameter),
      _Token('extends', SemanticTokenTypes.keyword),
      _Token('Object', SemanticTokenTypes.class_),
      _Token('var', SemanticTokenTypes.keyword),
      _Token('int', SemanticTokenTypes.class_),
      _Token('x', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('final', SemanticTokenTypes.keyword),
      _Token('int', SemanticTokenTypes.class_),
      _Token('y', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('0', SemanticTokenTypes.number),
      _Token('extends', SemanticTokenTypes.keyword),
      _Token('A', SemanticTokenTypes.class_),
      _Token('with', SemanticTokenTypes.keyword),
      _Token('M', SemanticTokenTypes.class_),
      _Token('implements', SemanticTokenTypes.keyword),
      _Token('B', SemanticTokenTypes.class_),
      _Token('this', SemanticTokenTypes.keyword),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_class_constructor_primary_invocation() async {
    var content = r'''
class A.named(int a, {int b = 0});

var a = [!A.named(1, b: 2);!]
''';

    var expected = [
      _Token('A', SemanticTokenTypes.class_, [
        CustomSemanticTokenModifiers.constructor,
      ]),
      _Token('named', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.constructor,
      ]),
      _Token('1', SemanticTokenTypes.number),
      _Token('b', SemanticTokenTypes.parameter, [
        CustomSemanticTokenModifiers.label,
      ]),
      _Token('2', SemanticTokenTypes.number),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_class_constructors() async {
    var content = '''
class MyClass {
  const MyClass();
  MyClass.named();
  factory MyClass.factory() => MyClass();
}

final a = MyClass();
final b = MyClass.named();
final c = MyClass.factory();
final d = MyClass.named;
const e = const MyClass();
''';

    var expected = [
      _Token('class', SemanticTokenTypes.keyword),
      _Token('MyClass', SemanticTokenTypes.class_, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('const', SemanticTokenTypes.keyword),
      _Token('MyClass', SemanticTokenTypes.class_, [
        CustomSemanticTokenModifiers.constructor,
        SemanticTokenModifiers.declaration,
      ]),
      _Token('MyClass', SemanticTokenTypes.class_, [
        CustomSemanticTokenModifiers.constructor,
        SemanticTokenModifiers.declaration,
      ]),
      _Token('named', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.constructor,
        SemanticTokenModifiers.declaration,
      ]),
      _Token('factory', SemanticTokenTypes.keyword),
      _Token('MyClass', SemanticTokenTypes.class_, [
        CustomSemanticTokenModifiers.constructor,
        SemanticTokenModifiers.declaration,
      ]),
      _Token('factory', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.constructor,
        SemanticTokenModifiers.declaration,
      ]),
      _Token('MyClass', 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.method, [
        CustomSemanticTokenModifiers.constructor,
      ]),
      _Token('final', SemanticTokenTypes.keyword),
      _Token('c', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('MyClass', SemanticTokenTypes.class_, [
        CustomSemanticTokenModifiers.constructor,
      ]),
      _Token('factory', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.constructor,
      ]),
      _Token('final', SemanticTokenTypes.keyword),
      _Token('d', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('MyClass', SemanticTokenTypes.class_),
      _Token('named', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.constructor,
      ]),
      _Token('const', SemanticTokenTypes.keyword),
      _Token('e', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('const', SemanticTokenTypes.keyword),
      _Token('MyClass', SemanticTokenTypes.class_, [
        CustomSemanticTokenModifiers.constructor,
      ]),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_class_constructors_factoryKeyword() async {
    var content = r'''
class A {
  A._();
[!
  factory() => A._();
  factory named() => A._();
!]
}
''';

    var expected = [
      _Token('factory', .keyword, [
        CustomSemanticTokenModifiers.constructor,
        .declaration,
      ]),
      _Token('A', .class_, [CustomSemanticTokenModifiers.constructor]),
      _Token('_', .method, [CustomSemanticTokenModifiers.constructor]),
      _Token('factory', .keyword, [
        CustomSemanticTokenModifiers.constructor,
        .declaration,
      ]),
      _Token('named', .method, [
        CustomSemanticTokenModifiers.constructor,
        .declaration,
      ]),
      _Token('A', .class_, [CustomSemanticTokenModifiers.constructor]),
      _Token('_', .method, [CustomSemanticTokenModifiers.constructor]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_class_constructors_newKeyword() async {
    var content = r'''
class A {
  new();
  new named();
}
void f() {
  A.new();
  A.named();
  A.new;
  A.named;
}
''';

    var expected = [
      _Token('class', .keyword),
      _Token('A', .class_, [.declaration]),
      _Token('new', .keyword, [
        CustomSemanticTokenModifiers.constructor,
        .declaration,
      ]),
      _Token('new', .keyword, [
        CustomSemanticTokenModifiers.constructor,
        .declaration,
      ]),
      _Token('named', .method, [
        CustomSemanticTokenModifiers.constructor,
        .declaration,
      ]),
      _Token('void', .keyword, [CustomSemanticTokenModifiers.void_]),
      _Token('f', .function, [.declaration, .static]),
      _Token('A', .class_, [CustomSemanticTokenModifiers.constructor]),
      _Token('new', .method, [CustomSemanticTokenModifiers.constructor]),
      _Token('A', .class_, [CustomSemanticTokenModifiers.constructor]),
      _Token('named', .method, [CustomSemanticTokenModifiers.constructor]),
      _Token('A', .class_),
      _Token('new', .method, [CustomSemanticTokenModifiers.constructor]),
      _Token('A', .class_),
      _Token('named', .method, [CustomSemanticTokenModifiers.constructor]),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_class_fields() async {
    var content = '''
class MyClass {
  /// field docs
  String myField = 'FieldVal';
  /// static field docs
  static String myStaticField = 'StaticFieldVal';
}

void f() {
  final a = MyClass();
  print(a.myField);
  MyClass.myStaticField = 'a';
}
''';

    var expected = [
      _Token('class', SemanticTokenTypes.keyword),
      _Token('MyClass', SemanticTokenTypes.class_, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('/// field docs', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token('String', SemanticTokenTypes.class_),
      _Token('myField', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token("'FieldVal'", SemanticTokenTypes.string),
      _Token('/// static field docs', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token('static', SemanticTokenTypes.keyword),
      _Token('String', SemanticTokenTypes.class_),
      _Token('myStaticField', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
        SemanticTokenModifiers.static,
      ]),
      _Token("'StaticFieldVal'", SemanticTokenTypes.string),
      _Token('void', SemanticTokenTypes.keyword, [
        SemanticTokenModifiers('void'),
      ]),
      _Token('f', SemanticTokenTypes.function, [
        SemanticTokenModifiers.declaration,
        SemanticTokenModifiers.static,
      ]),
      _Token('final', SemanticTokenTypes.keyword),
      _Token('a', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('MyClass', SemanticTokenTypes.class_, [
        CustomSemanticTokenModifiers.constructor,
      ]),
      _Token('print', SemanticTokenTypes.function),
      _Token('a', SemanticTokenTypes.variable),
      _Token('myField', SemanticTokenTypes.property, [
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token('MyClass', SemanticTokenTypes.class_),
      _Token('myStaticField', SemanticTokenTypes.property, [
        SemanticTokenModifiers.static,
      ]),
      _Token("'a'", SemanticTokenTypes.string),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_class_getterSetter() async {
    var content = '''
class MyClass {
  /// getter docs
  String get myGetter => 'GetterVal';
  /// setter docs
  set mySetter(String v) {}
  /// static getter docs
  static String get myStaticGetter => 'StaticGetterVal';
  /// static setter docs
  static set myStaticSetter(String staticV) {}
}

void f() {
  final a = MyClass();
  print(a.myGetter);
  a.mySetter = 'a';
}
''';

    var expected = [
      _Token('class', SemanticTokenTypes.keyword),
      _Token('MyClass', SemanticTokenTypes.class_, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('/// getter docs', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token('String', SemanticTokenTypes.class_),
      _Token('get', SemanticTokenTypes.keyword),
      _Token('myGetter', SemanticTokenTypes.property, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token("'GetterVal'", SemanticTokenTypes.string),
      _Token('/// setter docs', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token('set', SemanticTokenTypes.keyword),
      _Token('mySetter', SemanticTokenTypes.property, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token('String', SemanticTokenTypes.class_),
      _Token('v', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('/// static getter docs', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token('static', SemanticTokenTypes.keyword),
      _Token('String', SemanticTokenTypes.class_),
      _Token('get', SemanticTokenTypes.keyword),
      _Token('myStaticGetter', SemanticTokenTypes.property, [
        SemanticTokenModifiers.declaration,
        SemanticTokenModifiers.static,
      ]),
      _Token("'StaticGetterVal'", SemanticTokenTypes.string),
      _Token('/// static setter docs', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token('static', SemanticTokenTypes.keyword),
      _Token('set', SemanticTokenTypes.keyword),
      _Token('myStaticSetter', SemanticTokenTypes.property, [
        SemanticTokenModifiers.declaration,
        SemanticTokenModifiers.static,
      ]),
      _Token('String', SemanticTokenTypes.class_),
      _Token('staticV', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('void', SemanticTokenTypes.keyword, [
        SemanticTokenModifiers('void'),
      ]),
      _Token('f', SemanticTokenTypes.function, [
        SemanticTokenModifiers.declaration,
        SemanticTokenModifiers.static,
      ]),
      _Token('final', SemanticTokenTypes.keyword),
      _Token('a', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('MyClass', SemanticTokenTypes.class_, [
        CustomSemanticTokenModifiers.constructor,
      ]),
      _Token('print', SemanticTokenTypes.function),
      _Token('a', SemanticTokenTypes.variable),
      _Token('myGetter', SemanticTokenTypes.property, [
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token('a', SemanticTokenTypes.variable),
      _Token('mySetter', SemanticTokenTypes.property, [
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token("'a'", SemanticTokenTypes.string),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_class_method() async {
    var content = '''
class MyClass {
  /// method docs
  @override
  void myMethod() {}
  /// static method docs
  static void myStaticMethod() {
    // static method comment
  }
}

void f() {
  final a = MyClass();
  a.myMethod();
  MyClass.myStaticMethod();
  final b = a.myMethod;
  final c = MyClass.myStaticMethod;
}
''';

    var expected = [
      _Token('class', SemanticTokenTypes.keyword),
      _Token('MyClass', SemanticTokenTypes.class_, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('/// method docs', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token('@', CustomSemanticTokenTypes.annotation),
      _Token('override', SemanticTokenTypes.property, [
        CustomSemanticTokenModifiers.annotation,
      ]),
      _Token('void', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.void_,
      ]),
      _Token('myMethod', SemanticTokenTypes.method, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token('/// static method docs', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token('static', SemanticTokenTypes.keyword),
      _Token('void', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.void_,
      ]),
      _Token('myStaticMethod', SemanticTokenTypes.method, [
        SemanticTokenModifiers.declaration,
        SemanticTokenModifiers.static,
      ]),
      _Token('// static method comment', SemanticTokenTypes.comment),
      _Token('void', SemanticTokenTypes.keyword, [
        SemanticTokenModifiers('void'),
      ]),
      _Token('f', SemanticTokenTypes.function, [
        SemanticTokenModifiers.declaration,
        SemanticTokenModifiers.static,
      ]),
      _Token('final', SemanticTokenTypes.keyword),
      _Token('a', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('MyClass', SemanticTokenTypes.class_, [
        CustomSemanticTokenModifiers.constructor,
      ]),
      _Token('a', SemanticTokenTypes.variable),
      _Token('myMethod', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token('MyClass', SemanticTokenTypes.class_),
      _Token('myStaticMethod', SemanticTokenTypes.method, [
        SemanticTokenModifiers.static,
      ]),
      _Token('final', SemanticTokenTypes.keyword),
      _Token('b', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('a', SemanticTokenTypes.variable),
      _Token('myMethod', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token('final', SemanticTokenTypes.keyword),
      _Token('c', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('MyClass', SemanticTokenTypes.class_),
      _Token('myStaticMethod', SemanticTokenTypes.method, [
        SemanticTokenModifiers.static,
      ]),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_class_super() async {
    var content = '''
class A {
  A(int i) {}
  void f() {}
}

class B extends A {
[!
  B.b() : super(1);
  B(super.i);
  void f() {
    super.f();
  }
!]
}
''';

    var expected = [
      _Token('B', SemanticTokenTypes.class_, [
        CustomSemanticTokenModifiers.constructor,
        SemanticTokenModifiers.declaration,
      ]),
      _Token('b', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.constructor,
        SemanticTokenModifiers.declaration,
      ]),
      _Token('super', SemanticTokenTypes.keyword),
      _Token('1', SemanticTokenTypes.number),
      _Token('B', SemanticTokenTypes.class_, [
        CustomSemanticTokenModifiers.constructor,
        SemanticTokenModifiers.declaration,
      ]),
      _Token('super', SemanticTokenTypes.keyword),
      _Token('i', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('void', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.void_,
      ]),
      _Token('f', SemanticTokenTypes.method, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token('super', SemanticTokenTypes.keyword),
      _Token('f', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.instance,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_class_this() async {
    var content = '''
class A {
  int a;
  [!
  A(this.a);
  A.b() : this(1);
  void f() {
    this.f();
  }
  !]
}
''';

    var expected = [
      _Token('A', SemanticTokenTypes.class_, [
        CustomSemanticTokenModifiers.constructor,
        SemanticTokenModifiers.declaration,
      ]),
      _Token('this', SemanticTokenTypes.keyword),
      _Token('a', SemanticTokenTypes.variable, [
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token('A', SemanticTokenTypes.class_, [
        CustomSemanticTokenModifiers.constructor,
        SemanticTokenModifiers.declaration,
      ]),
      _Token('b', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.constructor,
        SemanticTokenModifiers.declaration,
      ]),
      _Token('1', SemanticTokenTypes.number),
      _Token('void', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.void_,
      ]),
      _Token('f', SemanticTokenTypes.method, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token('this', SemanticTokenTypes.keyword),
      _Token('f', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.instance,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_dartdoc() async {
    var content = '''
/// before [aaa] after
class MyClass {
  String? aaa;
}

/// before [bbb] after
int double(int bbb) => bbb * 2;
''';

    var expected = [
      _Token('/// before [', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token('aaa', SemanticTokenTypes.property, [
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token('] after', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token('class', SemanticTokenTypes.keyword),
      _Token('MyClass', SemanticTokenTypes.class_, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('String', SemanticTokenTypes.class_),
      _Token('aaa', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token('/// before [', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token('bbb', SemanticTokenTypes.parameter),
      _Token('] after', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token('int', SemanticTokenTypes.class_),
      _Token('double', SemanticTokenTypes.function, [
        SemanticTokenModifiers.declaration,
        SemanticTokenModifiers.static,
      ]),
      _Token('int', SemanticTokenTypes.class_),
      _Token('bbb', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('bbb', SemanticTokenTypes.parameter),
      _Token('2', SemanticTokenTypes.number),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_directives() async {
    failTestOnErrorDiagnostic = false; // Test has invalid imports.

    var content = '''
library foo;

import 'package:flutter/material.dart';
export 'package:flutter/widgets.dart';
import '../file.dart'
  if (dart.library.io) 'file_io.dart'
  if (dart.library.html) 'file_html.dart';
''';

    var expected = [
      _Token('library', SemanticTokenTypes.keyword),
      _Token('foo', SemanticTokenTypes.namespace),
      _Token('import', SemanticTokenTypes.keyword),
      _Token("'package:flutter/material.dart'", SemanticTokenTypes.string),
      _Token('export', SemanticTokenTypes.keyword),
      _Token("'package:flutter/widgets.dart'", SemanticTokenTypes.string),
      _Token('import', SemanticTokenTypes.keyword),
      _Token("'../file.dart'", SemanticTokenTypes.string),
      _Token('if', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.control,
      ]),
      _Token('dart', CustomSemanticTokenTypes.source),
      _Token('library', CustomSemanticTokenTypes.source),
      _Token('io', CustomSemanticTokenTypes.source),
      _Token("'file_io.dart'", SemanticTokenTypes.string),
      _Token('if', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.control,
      ]),
      _Token('dart', CustomSemanticTokenTypes.source),
      _Token('library', CustomSemanticTokenTypes.source),
      _Token('html', CustomSemanticTokenTypes.source),
      _Token("'file_html.dart'", SemanticTokenTypes.string),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_dotShorthand_constructor() async {
    failTestOnErrorDiagnostic = false;

    var content = r'''
class A {
  A();
  A.named(int x);
}
void f() {
  [!
  A a = .new();
  A aa = .named(42);
  A aTearOff = .new;
  A aTearOff = .named;
  !]
}
''';

    var expected = [
      _Token('A', SemanticTokenTypes.class_),
      _Token('a', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('new', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.constructor,
      ]),
      _Token('A', SemanticTokenTypes.class_),
      _Token('aa', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('named', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.constructor,
      ]),
      _Token('42', SemanticTokenTypes.number),
      _Token('A', SemanticTokenTypes.class_),
      _Token('aTearOff', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('new', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.constructor,
      ]),
      _Token('A', SemanticTokenTypes.class_),
      _Token('aTearOff', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('named', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.constructor,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_dotShorthand_getter() async {
    var content = r'''
enum E { a }
class A {
  static A get aGetter => A();
}
extension type B(int x) {
  static B get bGetter => B(1);
}
class C {}
class D extends C with Mixin {}
mixin Mixin on C {
  static Mixin get dGetter => D();
}
void f() {
  [!
  E e = .a;
  A a = .aGetter;
  B b = .bGetter;
  Mixin m = .dGetter;
  !]
}
''';

    var expected = [
      _Token('E', SemanticTokenTypes.enum_),
      _Token('e', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('a', SemanticTokenTypes.enumMember),
      _Token('A', SemanticTokenTypes.class_),
      _Token('a', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('aGetter', SemanticTokenTypes.property, [
        SemanticTokenModifiers.static,
      ]),
      _Token('B', SemanticTokenTypes.class_),
      _Token('b', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('bGetter', SemanticTokenTypes.property, [
        SemanticTokenModifiers.static,
      ]),
      _Token('Mixin', SemanticTokenTypes.class_),
      _Token('m', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('dGetter', SemanticTokenTypes.property, [
        SemanticTokenModifiers.static,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_dotShorthand_method() async {
    failTestOnErrorDiagnostic = false;

    var content = r'''
class A {
  static A aMethod() => A();
}
extension type B(int x) {
  static B bMethod() => B(1);
}
class C {}
class D extends C with Mixin {}
mixin Mixin on C {
  static Mixin dMethod() => D();
}
void f() {
  [!
  A a = .aMethod();
  A aa = .aMethod;
  B b = .bMethod();
  B bb = .bMethod;
  Mixin m = .dMethod();
  Mixin mm = .dMethod;
  !]
}
''';

    var expected = [
      _Token('A', SemanticTokenTypes.class_),
      _Token('a', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('aMethod', SemanticTokenTypes.method, [
        SemanticTokenModifiers.static,
      ]),
      _Token('A', SemanticTokenTypes.class_),
      _Token('aa', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('aMethod', SemanticTokenTypes.method, [
        SemanticTokenModifiers.static,
      ]),
      _Token('B', SemanticTokenTypes.class_),
      _Token('b', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('bMethod', SemanticTokenTypes.method, [
        SemanticTokenModifiers.static,
      ]),
      _Token('B', SemanticTokenTypes.class_),
      _Token('bb', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('bMethod', SemanticTokenTypes.method, [
        SemanticTokenModifiers.static,
      ]),
      _Token('Mixin', SemanticTokenTypes.class_),
      _Token('m', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('dMethod', SemanticTokenTypes.method, [
        SemanticTokenModifiers.static,
      ]),
      _Token('Mixin', SemanticTokenTypes.class_),
      _Token('mm', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('dMethod', SemanticTokenTypes.method, [
        SemanticTokenModifiers.static,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_emptyAnalysisRoots_handlesFileRequestsImmediately() async {
    var content = '''
// test
''';

    var code = TestCode.parseNormalized(content);
    newFile(mainFilePath, code.code);
    await initialize(allowEmptyRootUri: true);

    unawaited(openFile(mainFileUri, code.code));
    var tokens = await getSemanticTokens(mainFileUri);
    expect(tokens.data, isNotEmpty);
  }

  Future<void> test_extension() async {
    var content = '''
extension A on String {}
''';

    var expected = [
      _Token('extension', SemanticTokenTypes.keyword),
      _Token('A', SemanticTokenTypes.class_),
      _Token('on', SemanticTokenTypes.keyword),
      _Token('String', SemanticTokenTypes.class_),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_extensionType() async {
    var content = '''
extension type E(int i) {}
''';

    var expected = [
      _Token('extension', SemanticTokenTypes.keyword),
      _Token('type', SemanticTokenTypes.keyword),
      _Token('E', SemanticTokenTypes.class_, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('int', SemanticTokenTypes.class_),
      _Token('i', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
      ]),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_fromPlugin() async {
    var pluginAnalyzedFilePath = join(projectFolderPath, 'lib', 'foo.foo');
    var pluginAnalyzedFileUri = pathContext.toUri(pluginAnalyzedFilePath);
    var content = 'CLASS STRING VARIABLE';
    var code = TestCode.parseNormalized(content);

    var expected = [
      _Token('CLASS', SemanticTokenTypes.class_),
      _Token('STRING', SemanticTokenTypes.string),
      _Token('VARIABLE', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
    ];

    await initialize();
    await openFile(pluginAnalyzedFileUri, code.code);

    var pluginResult = plugin.AnalysisHighlightsParams(pluginAnalyzedFilePath, [
      plugin.HighlightRegion(plugin.HighlightRegionType.CLASS, 0, 5),
      plugin.HighlightRegion(plugin.HighlightRegionType.LITERAL_STRING, 6, 6),
      plugin.HighlightRegion(
        plugin.HighlightRegionType.LOCAL_VARIABLE_DECLARATION,
        13,
        8,
      ),
    ]);
    configureTestPlugin(notification: pluginResult.toNotification());

    var tokens = await getSemanticTokens(pluginAnalyzedFileUri);
    var decoded = _decodeSemanticTokens(content, tokens);
    expect(decoded, equals(expected));
  }

  Future<void> test_function_callMethod_invocation() async {
    var content = r'''
f(void Function(int)? x) {
  [!x?.call(2);!]
}
''';

    var expected = [
      _Token('x', SemanticTokenTypes.parameter),
      _Token('call', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token('2', SemanticTokenTypes.number),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_function_callMethod_invocation_extension() async {
    var content = r'''
extension on void Function() {
  m() => [!call()!];
}
''';

    var expected = <_Token>[
      _Token('call', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.instance,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_function_callMethod_propertyAccess() async {
    var content = r'''
extension on void Function()? {
  m() => [!this?.call!];
}
''';

    var expected = <_Token>[
      _Token('this', SemanticTokenTypes.keyword),
      _Token('call', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.instance,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_function_callMethod_simpleIdentifier() async {
    var content = r'''
extension on void Function() {
  m() {
    [!call!];
  }
}
''';

    var expected = <_Token>[
      _Token('call', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.instance,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_function_callMethod_simpleIdentifier_argument() async {
    var content = r'''
extension on void Function() {
  m(void Function() f) {
    m([!call!]);
  }
}
''';

    var expected = <_Token>[
      _Token('call', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.instance,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_function_callMethod_simpleIdentifier_assignment() async {
    var content = r'''
extension on void Function() {
  m() {
    var a;
    a = [!call!];
  }
}
''';

    var expected = <_Token>[
      _Token('call', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.instance,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void>
  test_function_callMethod_simpleIdentifier_expressionFunctionBody() async {
    var content = r'''
extension on void Function() {
  m() => [!call!];
}
''';

    var expected = <_Token>[
      _Token('call', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.instance,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_function_callMethod_simpleIdentifier_return() async {
    var content = r'''
extension on void Function() {
  m() {
    return [!call!];
  }
}
''';

    var expected = <_Token>[
      _Token('call', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.instance,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void>
  test_function_callMethod_simpleIdentifier_variableDeclaration() async {
    var content = r'''
extension on void Function() {
  m() {
    var _ = [!call!];
  }
}
''';

    var expected = <_Token>[
      _Token('call', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.instance,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_function_callMethod_tearOff() async {
    var content = r'''
f(void Function(int) x) {
  [!x.call!];
}
''';

    var expected = [
      _Token('x', SemanticTokenTypes.parameter),
      _Token('call', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.instance,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_functionType_callMethod_invocation_extension() async {
    var content = r'''
extension on Function {
  m() => [!call()!];
}
''';

    var expected = <_Token>[
      _Token('call', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.instance,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_functionType_callMethod_tearOff() async {
    var content = r'''
f(Function x) {
  [!x.call!];
}
''';

    var expected = [
      _Token('x', SemanticTokenTypes.parameter),
      _Token('call', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.instance,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  /// Verify that sending a semantic token request immediately after an overlay
  /// update (with no delay) does not result in corrupt semantic tokens because
  /// the previous file content was used.
  ///
  /// https://github.com/dart-lang/sdk/issues/55084
  Future<void> test_immediatelyAfterUpdate() async {
    var initialContent = normalizeNewlinesForPlatform('''class A {}
class B {}''');
    var updatedContent = normalizeNewlinesForPlatform('''class Aaaaa {}
class Bbbbb {}''');

    newFile(mainFilePath, initialContent);
    await initialize();

    await openFile(mainFileUri, initialContent);

    // Send an edit (don't await), then fetch the tokens and verify the results
    // were correct for the final content. If the bug occurs, the strings won't
    // match up because the offsets will have been mapped incorrectly.
    unawaited(replaceFile(2, mainFileUri, updatedContent));
    var tokens = await getSemanticTokens(mainFileUri);
    var decoded = _decodeSemanticTokens(updatedContent, tokens);
    expect(decoded, [
      _Token('class', SemanticTokenTypes.keyword),
      _Token('Aaaaa', SemanticTokenTypes.class_, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('class', SemanticTokenTypes.keyword),
      _Token('Bbbbb', SemanticTokenTypes.class_, [
        SemanticTokenModifiers.declaration,
      ]),
    ]);
  }

  Future<void> test_initializer() async {
    var content = '''
class A {
  final String a;
  [!A(String a) : a = a!];
}
''';

    var expected = [
      _Token('A', SemanticTokenTypes.class_, [
        CustomSemanticTokenModifiers.constructor,
        SemanticTokenModifiers.declaration,
      ]),
      _Token('String', SemanticTokenTypes.class_),
      _Token('a', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('a', SemanticTokenTypes.variable, [
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token('a', SemanticTokenTypes.parameter),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_invalidSyntax() async {
    failTestOnErrorDiagnostic = false;

    var content = '''
/// class docs
class MyClass {
  // class comment
}

this is not valid code.

/// class docs 2
class MyClass2 {
  // class comment 2
}
''';
    var code = TestCode.parseNormalized(content);

    // Expect the correct tokens for the valid code before/after but don't
    // check the tokens for the invalid code as there are no concrete
    // expectations for them.
    var expected1 = [
      _Token('/// class docs', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token('class', SemanticTokenTypes.keyword),
      _Token('MyClass', SemanticTokenTypes.class_, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('// class comment', SemanticTokenTypes.comment),
    ];
    var expected2 = [
      _Token('/// class docs 2', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token('class', SemanticTokenTypes.keyword),
      _Token('MyClass2', SemanticTokenTypes.class_, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('// class comment 2', SemanticTokenTypes.comment),
    ];

    await initialize();
    await openFile(mainFileUri, code.code);

    var tokens = await getSemanticTokens(mainFileUri);
    var decoded = _decodeSemanticTokens(code.code, tokens);

    // Remove the tokens between the two expected sets.
    decoded.removeRange(expected1.length, decoded.length - expected2.length);

    expect(decoded, equals([...expected1, ...expected2]));
  }

  Future<void> test_keywords() async {
    // "control" keywords should be tagged with a modifier so the client
    // can color them differently to other keywords.
    var content = r'''
void f() async {
  var a = new Object();
  await null;
  if (false) {
    print('test');
  }
  for (var item in []);
  switch (1) {
    case int(:var isEven) when isEven:
  }
}
''';

    var expected = [
      _Token('void', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.void_,
      ]),
      _Token('f', SemanticTokenTypes.function, [
        SemanticTokenModifiers.declaration,
        SemanticTokenModifiers.static,
      ]),
      _Token('async', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.control,
      ]),
      _Token('var', SemanticTokenTypes.keyword),
      _Token('a', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('new', SemanticTokenTypes.keyword),
      _Token('Object', SemanticTokenTypes.class_, [
        CustomSemanticTokenModifiers.constructor,
      ]),
      _Token('await', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.control,
      ]),
      _Token('null', SemanticTokenTypes.keyword),
      _Token('if', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.control,
      ]),
      _Token('false', CustomSemanticTokenTypes.boolean),
      _Token('print', SemanticTokenTypes.function),
      _Token("'test'", SemanticTokenTypes.string),
      _Token('for', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.control,
      ]),
      _Token('var', SemanticTokenTypes.keyword),
      _Token('item', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('in', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.control,
      ]),
      _Token('switch', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.control,
      ]),
      _Token('1', SemanticTokenTypes.number),
      _Token('case', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.control,
      ]),
      _Token('int', SemanticTokenTypes.class_),
      _Token('var', SemanticTokenTypes.keyword),
      _Token('isEven', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('when', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.control,
      ]),
      _Token('isEven', SemanticTokenTypes.variable),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_label() async {
    var content = '''
void f() {
myLabel:
  while (true) {
    break myLabel;
  }
}
''';

    var expected = [
      _Token('void', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.void_,
      ]),
      _Token('f', SemanticTokenTypes.function, [
        SemanticTokenModifiers.declaration,
        SemanticTokenModifiers.static,
      ]),
      _Token('myLabel', CustomSemanticTokenTypes.label, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('while', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.control,
      ]),
      _Token('true', CustomSemanticTokenTypes.boolean),
      _Token('break', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.control,
      ]),
      _Token('myLabel', CustomSemanticTokenTypes.label),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_lastLine_code() async {
    var content = 'String? bar;';

    var expected = [
      _Token('String', SemanticTokenTypes.class_),
      _Token('bar', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_lastLine_comment() async {
    var content = '// Trailing comment';

    var expected = [_Token('// Trailing comment', SemanticTokenTypes.comment)];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_lastLine_multilineComment() async {
    var content = '''
/**
 * Trailing comment
 */''';

    var expected = [
      _Token('/**$eol', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token(' * Trailing comment$eol', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token(' */', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_local() async {
    var content = '''
void f() {
  func(String a) => print(a);
  final funcTearOff = func;
}
''';

    var expected = [
      _Token('void', SemanticTokenTypes.keyword, [
        SemanticTokenModifiers('void'),
      ]),
      _Token('f', SemanticTokenTypes.function, [
        SemanticTokenModifiers.declaration,
        SemanticTokenModifiers.static,
      ]),
      _Token('func', SemanticTokenTypes.function, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('String', SemanticTokenTypes.class_),
      _Token('a', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('print', SemanticTokenTypes.function),
      _Token('a', SemanticTokenTypes.parameter),
      _Token('final', SemanticTokenTypes.keyword),
      _Token('funcTearOff', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('func', SemanticTokenTypes.function),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_manyBools_bug() async {
    // Similar to test_manyImports_sortBug, this code triggered inconsistent tokens
    // for "false" because tokens were sorted incorrectly (because both boolean and
    // keyword had the same offset and length, which is all that were sorted by).
    var content = '''
class MyTestClass {
/// test
/// test
bool test1 = false;

/// test
/// test
bool test2 = false;

/// test
/// test
bool test3 = false;

/// test
/// test
bool test4 = false;

/// test
/// test
bool test5 = false;

/// test
/// test
bool test6 = false;
}
''';

    var expected = [
      _Token('class', SemanticTokenTypes.keyword),
      _Token('MyTestClass', SemanticTokenTypes.class_, [
        SemanticTokenModifiers.declaration,
      ]),
      for (var i = 1; i <= 6; i++) ...[
        _Token('/// test', SemanticTokenTypes.comment, [
          SemanticTokenModifiers.documentation,
        ]),
        _Token('/// test', SemanticTokenTypes.comment, [
          SemanticTokenModifiers.documentation,
        ]),
        _Token('bool', SemanticTokenTypes.class_),
        _Token('test$i', SemanticTokenTypes.variable, [
          SemanticTokenModifiers.declaration,
          CustomSemanticTokenModifiers.instance,
        ]),
        _Token('false', CustomSemanticTokenTypes.boolean),
      ],
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_manyImports_sortBug() async {
    // This test is for a bug where some "import" tokens would not be
    // highlighted correctly. Imports are made up of a DIRECTIVE token that
    // spans a KEYWORD ("import") and LITERAL_STRING. The original code sorted
    // by only offset when handling overlapping tokens, which for certain lists
    // (such as the one created for the code below) would result in the KEYWORD
    // coming before the DIRECTIVE, which resulted in the DIRECTIVE overwriting
    // it.
    var content = '''
import 'dart:async';
import 'dart:async';
import 'dart:async';
import 'dart:async';
import 'dart:async';
import 'dart:async';
import 'dart:async';
import 'dart:async';
import 'dart:async';
import 'dart:async';
import 'dart:async';
import 'dart:async';
import 'dart:async';
''';

    var expected = [
      for (var i = 0; i < 13; i++) ...[
        _Token('import', SemanticTokenTypes.keyword),
        _Token("'dart:async'", SemanticTokenTypes.string),
      ],
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_mixin() async {
    var content = '''
mixin M on C {}
class C {}
''';

    var expected = [
      _Token('mixin', SemanticTokenTypes.keyword),
      _Token('M', SemanticTokenTypes.class_),
      _Token('on', SemanticTokenTypes.keyword),
      _Token('C', SemanticTokenTypes.class_),
      _Token('class', SemanticTokenTypes.keyword),
      _Token('C', SemanticTokenTypes.class_, [
        SemanticTokenModifiers.declaration,
      ]),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_multilineRegions() async {
    var content = '''
/**
 * This is my class comment
 *
 * There are
 * multiple lines
 */
class MyClass {}
''';

    var expected = [
      _Token('/**$eol', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token(' * This is my class comment$eol', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token(' *$eol', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token(' * There are$eol', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token(' * multiple lines$eol', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token(' */', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token('class', SemanticTokenTypes.keyword),
      _Token('MyClass', SemanticTokenTypes.class_, [
        SemanticTokenModifiers.declaration,
      ]),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_namedArguments() async {
    var content = '''
f({String? a, dynamic b}) {
  f(a: a, b: b);
}
''';

    var expected = [
      _Token('f', SemanticTokenTypes.function, [
        SemanticTokenModifiers.declaration,
        SemanticTokenModifiers.static,
      ]),
      _Token('String', SemanticTokenTypes.class_),
      _Token('a', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('dynamic', SemanticTokenTypes.type),
      _Token('b', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('f', SemanticTokenTypes.function),
      _Token('a', SemanticTokenTypes.parameter, [
        CustomSemanticTokenModifiers.label,
      ]),
      _Token('a', SemanticTokenTypes.parameter),
      _Token('b', SemanticTokenTypes.parameter, [
        CustomSemanticTokenModifiers.label,
      ]),
      _Token('b', SemanticTokenTypes.parameter),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_namedRecordFields_extension() async {
    var content = '''
extension on ({int field,}) {
  get other => field + this.field;
}
''';

    var expected = [
      _Token('extension', SemanticTokenTypes.keyword),
      _Token('on', SemanticTokenTypes.keyword),
      _Token('int', SemanticTokenTypes.class_),
      _Token('get', SemanticTokenTypes.keyword),
      _Token('other', SemanticTokenTypes.property, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token('field', SemanticTokenTypes.property, [
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token('this', SemanticTokenTypes.keyword),
      _Token('field', SemanticTokenTypes.property, [
        CustomSemanticTokenModifiers.instance,
      ]),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_never() async {
    var content = '''
Never f() => throw '';
Never? g() => throw '';
''';

    var expected = [
      _Token('Never', SemanticTokenTypes.type),
      _Token('f', SemanticTokenTypes.function, [
        SemanticTokenModifiers.declaration,
        SemanticTokenModifiers.static,
      ]),
      _Token('throw', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.control,
      ]),
      _Token("''", SemanticTokenTypes.string),
      _Token('Never', SemanticTokenTypes.type),
      _Token('g', SemanticTokenTypes.function, [
        SemanticTokenModifiers.declaration,
        SemanticTokenModifiers.static,
      ]),
      _Token('throw', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.control,
      ]),
      _Token("''", SemanticTokenTypes.string),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_parameter_fieldFormal() async {
    var content = '''
class A {
  final String a;
  A([!this.a!]);
}
''';

    var expected = [
      _Token('this', SemanticTokenTypes.keyword),
      _Token('a', SemanticTokenTypes.variable, [
        CustomSemanticTokenModifiers.instance,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_parameter_super() async {
    var content = '''
class A {
  A({String? a, String? b});
}

class B extends A {
  [!B({super.a}) : super(b: a)!];
}
''';

    var expected = [
      _Token('B', SemanticTokenTypes.class_, [
        CustomSemanticTokenModifiers.constructor,
        SemanticTokenModifiers.declaration,
      ]),
      _Token('super', SemanticTokenTypes.keyword),
      _Token('a', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('super', SemanticTokenTypes.keyword),
      _Token('b', SemanticTokenTypes.parameter, [
        CustomSemanticTokenModifiers.label,
      ]),
      _Token('a', SemanticTokenTypes.parameter),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_parameter_super_fieldFormal() async {
    var content = '''
class A {
  String a;
  A(this.a);
}

class B extends A {
  B([!super.a!]);
}
''';

    var expected = [
      _Token('super', SemanticTokenTypes.keyword),
      _Token('a', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_parameter_super_fieldFormal_unresolved() async {
    failTestOnErrorDiagnostic = false;

    var content = '''
class A {
  A(this.a);
}

class B extends A {
  B([!super.a!]);
}
''';

    var expected = [
      _Token('super', SemanticTokenTypes.keyword),
      _Token('a', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_parameter_super_unresolved() async {
    failTestOnErrorDiagnostic = false;

    var content = '''
class A {
  A();
}

class B extends A {
  B([!super.a!]);
}
''';

    var expected = [
      _Token('super', SemanticTokenTypes.keyword),
      _Token('a', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_parameter_this() async {
    var content = '''
class A {
  String a;
  A([!this.a!]);
}
''';

    var expected = [
      _Token('this', SemanticTokenTypes.keyword),
      _Token('a', SemanticTokenTypes.variable, [
        CustomSemanticTokenModifiers.instance,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_parameter_this_unresolved() async {
    failTestOnErrorDiagnostic = false;

    var content = '''
class A {
  A([!this.a!]);
}
''';

    var expected = [
      _Token('this', SemanticTokenTypes.keyword),
      _Token('a', SemanticTokenTypes.variable, [
        CustomSemanticTokenModifiers.instance,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_patterns_assignment() async {
    var content = r'''
void f() {
  int a, b;
  <int>[a, b] = [1, 2];
  var [c, d] = [1, 2];
}
''';

    var expected = [
      _Token('void', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.void_,
      ]),
      _Token('f', SemanticTokenTypes.function, [
        SemanticTokenModifiers.declaration,
        SemanticTokenModifiers.static,
      ]),
      _Token('int', SemanticTokenTypes.class_),
      _Token('a', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('b', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('int', SemanticTokenTypes.class_),
      _Token('a', SemanticTokenTypes.variable),
      _Token('b', SemanticTokenTypes.variable),
      _Token('1', SemanticTokenTypes.number),
      _Token('2', SemanticTokenTypes.number),
      _Token('var', SemanticTokenTypes.keyword),
      _Token('c', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('d', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('1', SemanticTokenTypes.number),
      _Token('2', SemanticTokenTypes.number),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_patterns_switch_list() async {
    var content = r'''
void f() {
  switch (1) {
    case [var c, == 'a'] when c != null:
  }
}
''';

    var expected = [
      _Token('void', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.void_,
      ]),
      _Token('f', SemanticTokenTypes.function, [
        SemanticTokenModifiers.declaration,
        SemanticTokenModifiers.static,
      ]),
      _Token('switch', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.control,
      ]),
      _Token('1', SemanticTokenTypes.number),
      _Token('case', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.control,
      ]),
      _Token('var', SemanticTokenTypes.keyword),
      _Token('c', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token("'a'", SemanticTokenTypes.string),
      _Token('when', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.control,
      ]),
      _Token('c', SemanticTokenTypes.variable),
      _Token('null', SemanticTokenTypes.keyword),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_patterns_switch_object() async {
    var content = r'''
void f() {
  switch (1) {
    case int(isEven: var isEven, toString: var toString) when isEven:
      isEven;
      toString;
  }
}
''';

    var expected = [
      _Token('void', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.void_,
      ]),
      _Token('f', SemanticTokenTypes.function, [
        SemanticTokenModifiers.declaration,
        SemanticTokenModifiers.static,
      ]),
      _Token('switch', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.control,
      ]),
      _Token('1', SemanticTokenTypes.number),
      _Token('case', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.control,
      ]),
      _Token('int', SemanticTokenTypes.class_),
      _Token('isEven', SemanticTokenTypes.property, [
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token('var', SemanticTokenTypes.keyword),
      _Token('isEven', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('toString', SemanticTokenTypes.method, [
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token('var', SemanticTokenTypes.keyword),
      _Token('toString', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('when', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.control,
      ]),
      _Token('isEven', SemanticTokenTypes.variable),
      _Token('isEven', SemanticTokenTypes.variable),
      _Token('toString', SemanticTokenTypes.variable),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_patterns_switch_object_inferredName() async {
    var content = r'''
void f() {
  switch (1) {
    case int(:var isEven) when isEven:
  }
}
''';

    var expected = [
      _Token('void', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.void_,
      ]),
      _Token('f', SemanticTokenTypes.function, [
        SemanticTokenModifiers.declaration,
        SemanticTokenModifiers.static,
      ]),
      _Token('switch', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.control,
      ]),
      _Token('1', SemanticTokenTypes.number),
      _Token('case', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.control,
      ]),
      _Token('int', SemanticTokenTypes.class_),
      _Token('var', SemanticTokenTypes.keyword),
      _Token('isEven', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('when', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.control,
      ]),
      _Token('isEven', SemanticTokenTypes.variable),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_positionalRecordFields_extension() async {
    var content = r'''
extension on (int field, double,) {
  get other => $1 + $2;
}
''';

    var expected = [
      _Token('extension', SemanticTokenTypes.keyword),
      _Token('on', SemanticTokenTypes.keyword),
      _Token('int', SemanticTokenTypes.class_),
      _Token('double', SemanticTokenTypes.class_),
      _Token('get', SemanticTokenTypes.keyword),
      _Token('other', SemanticTokenTypes.property, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token(r'$1', SemanticTokenTypes.property, [
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token(r'$2', SemanticTokenTypes.property, [
        CustomSemanticTokenModifiers.instance,
      ]),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_range() async {
    var content = '''
/// class docs
class [!MyClass<T> {
  // class comment
}!]

// Trailing comment
''';

    var expected = [
      _Token('MyClass', SemanticTokenTypes.class_, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('T', SemanticTokenTypes.typeParameter),
      _Token('// class comment', SemanticTokenTypes.comment),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_range_entireFile() async {
    var content = '''[!
/// class docs
class MyClass<T> {
  // class comment
}

// Trailing comment
!]''';

    var expected = [
      _Token('/// class docs', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token('class', SemanticTokenTypes.keyword),
      _Token('MyClass', SemanticTokenTypes.class_, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('T', SemanticTokenTypes.typeParameter),
      _Token('// class comment', SemanticTokenTypes.comment),
      _Token('// Trailing comment', SemanticTokenTypes.comment),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_range_multilineRegions() async {
    var content = '''
/**
 * This is my class comment
 *
 * [!There are
 * multiple lines
 */
class!] MyClass {}
''';

    var expected = [
      _Token(' * There are$eol', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token(' * multiple lines$eol', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token(' */', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token('class', SemanticTokenTypes.keyword),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_record_fields() async {
    failTestOnErrorDiagnostic = false; // Unresolved symbols.

    var content = r'''
void f((int, {int field1}) record) {
  [!
  record.$1;
  record.field1;
  (1,).$1;
  (field1: 1).field1;
  (1,).unresolved;
  !]
}
''';

    var expected = [
      _Token('record', SemanticTokenTypes.parameter),
      _Token(r'$1', SemanticTokenTypes.property, [
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token('record', SemanticTokenTypes.parameter),
      _Token('field1', SemanticTokenTypes.property, [
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token('1', SemanticTokenTypes.number),
      _Token(r'$1', SemanticTokenTypes.property, [
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token('field1', SemanticTokenTypes.parameter),
      _Token('1', SemanticTokenTypes.number),
      _Token('field1', SemanticTokenTypes.property, [
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token('1', SemanticTokenTypes.number),
      _Token('unresolved', CustomSemanticTokenTypes.source),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_sort_sameOffsets() async {
    // This code initially (before merging) produces a String token starting at
    // offset 11 (as it drops out of one interpolated variable) and then a new
    // Interpolatation token.
    // This test is to ensure the assertion in `offsetLengthPrioritySort` does
    // not trigger (as it does if length is ignored, which was a bug).
    var content = r'''
var s = '';
var a = [!'$s$s'!];
''';

    var expected = [
      _Token("'", SemanticTokenTypes.string),
      _Token(r'$', CustomSemanticTokenTypes.source, [
        CustomSemanticTokenModifiers.interpolation,
      ]),
      _Token('s', SemanticTokenTypes.property),
      _Token(r'$', CustomSemanticTokenTypes.source, [
        CustomSemanticTokenModifiers.interpolation,
      ]),
      _Token('s', SemanticTokenTypes.property),
      _Token("'", SemanticTokenTypes.string),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_strings() async {
    var content = '''
String foo(String c) => c;
const string1 = 'test';
var string2 = 'test1 \$string1 test2 \${foo('a' + 'b')}';
const string3 = r'\$string1 \${string1.length}';
const string4 = \'\'\'
multi
  line
    string
\'\'\';
''';

    var expected = [
      _Token('String', SemanticTokenTypes.class_),
      _Token('foo', SemanticTokenTypes.function, [
        SemanticTokenModifiers.declaration,
        SemanticTokenModifiers.static,
      ]),
      _Token('String', SemanticTokenTypes.class_),
      _Token('c', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('c', SemanticTokenTypes.parameter),

      _Token('const', SemanticTokenTypes.keyword),
      _Token('string1', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token("'test'", SemanticTokenTypes.string),

      _Token('var', SemanticTokenTypes.keyword),
      _Token('string2', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token(r"'test1 ", SemanticTokenTypes.string),
      _Token(r'$', CustomSemanticTokenTypes.source, [
        CustomSemanticTokenModifiers.interpolation,
      ]),
      _Token('string1', SemanticTokenTypes.property),
      _Token(' test2 ', SemanticTokenTypes.string),
      _Token(r'${', CustomSemanticTokenTypes.source, [
        CustomSemanticTokenModifiers.interpolation,
      ]),
      _Token('foo', SemanticTokenTypes.function),
      _Token('(', CustomSemanticTokenTypes.source, [
        CustomSemanticTokenModifiers.interpolation,
      ]),
      _Token("'a'", SemanticTokenTypes.string),
      _Token(' + ', CustomSemanticTokenTypes.source, [
        CustomSemanticTokenModifiers.interpolation,
      ]),
      _Token("'b'", SemanticTokenTypes.string),
      _Token(')}', CustomSemanticTokenTypes.source, [
        CustomSemanticTokenModifiers.interpolation,
      ]),
      _Token("'", SemanticTokenTypes.string),

      // string3 is raw and should be treated as a single string.
      _Token('const', SemanticTokenTypes.keyword),
      _Token('string3', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token(r"r'$string1 ${string1.length}'", SemanticTokenTypes.string),
      _Token('const', SemanticTokenTypes.keyword),

      _Token('string4', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token("'''$eol", SemanticTokenTypes.string),
      _Token('multi$eol', SemanticTokenTypes.string),
      _Token('  line$eol', SemanticTokenTypes.string),
      _Token('    string$eol', SemanticTokenTypes.string),
      _Token("'''", SemanticTokenTypes.string),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_strings_escape() async {
    failTestOnErrorDiagnostic = false; // Last unicode escape is invalid.

    // The 9's in these strings are not part of the escapes (they make the
    // strings too long).
    var content = r'''
const string1 = 'it\'s escaped\\\n\$';
const string2 = 'hex \x12\x1299';
const string3 = 'unicode \u1234\u123499\u{123456}\u{12345699}';
const string4 = "\"";
''';

    var expected = [
      _Token('const', SemanticTokenTypes.keyword),
      _Token('string1', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token("'it", SemanticTokenTypes.string),
      _Token(r"\'", SemanticTokenTypes.string, [
        CustomSemanticTokenModifiers.escape,
      ]),
      _Token('s escaped', SemanticTokenTypes.string),
      _Token(r'\\', SemanticTokenTypes.string, [
        CustomSemanticTokenModifiers.escape,
      ]),
      _Token(r'\n', SemanticTokenTypes.string, [
        CustomSemanticTokenModifiers.escape,
      ]),
      _Token(r'\$', SemanticTokenTypes.string, [
        CustomSemanticTokenModifiers.escape,
      ]),
      _Token(r"'", SemanticTokenTypes.string),
      _Token('const', SemanticTokenTypes.keyword),
      _Token('string2', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token("'hex ", SemanticTokenTypes.string),
      _Token(r'\x12', SemanticTokenTypes.string, [
        CustomSemanticTokenModifiers.escape,
      ]),
      _Token(r'\x12', SemanticTokenTypes.string, [
        CustomSemanticTokenModifiers.escape,
      ]),
      // The 99 is not part of the escape
      _Token("99'", SemanticTokenTypes.string),
      _Token('const', SemanticTokenTypes.keyword),
      _Token('string3', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token("'unicode ", SemanticTokenTypes.string),
      _Token(r'\u1234', SemanticTokenTypes.string, [
        CustomSemanticTokenModifiers.escape,
      ]),
      _Token(r'\u1234', SemanticTokenTypes.string, [
        CustomSemanticTokenModifiers.escape,
      ]),
      // The 99 is not part of the escape
      _Token('99', SemanticTokenTypes.string),
      _Token(r'\u{123456}', SemanticTokenTypes.string, [
        CustomSemanticTokenModifiers.escape,
      ]),
      // The 99 makes this invalid so i's not an escape
      _Token(r"\u{12345699}'", SemanticTokenTypes.string),
      _Token('const', SemanticTokenTypes.keyword),
      _Token('string4', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('"', SemanticTokenTypes.string),
      _Token(r'\"', SemanticTokenTypes.string, [
        CustomSemanticTokenModifiers.escape,
      ]),
      _Token('"', SemanticTokenTypes.string),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_strings_escape_interpolation1() async {
    var content = r'''
const value = 1;
const string1 = 'it\'s $value escaped\\\n';
''';

    var expected = [
      _Token('const', SemanticTokenTypes.keyword),
      _Token('value', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('1', SemanticTokenTypes.number),
      _Token('const', SemanticTokenTypes.keyword),
      _Token('string1', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token("'it", SemanticTokenTypes.string),
      _Token(r"\'", SemanticTokenTypes.string, [
        CustomSemanticTokenModifiers.escape,
      ]),
      _Token('s ', SemanticTokenTypes.string),
      _Token(r'$', CustomSemanticTokenTypes.source, [
        CustomSemanticTokenModifiers.interpolation,
      ]),
      _Token('value', SemanticTokenTypes.property),
      _Token(' escaped', SemanticTokenTypes.string),
      _Token(r'\\', SemanticTokenTypes.string, [
        CustomSemanticTokenModifiers.escape,
      ]),
      _Token(r'\n', SemanticTokenTypes.string, [
        CustomSemanticTokenModifiers.escape,
      ]),
      _Token(r"'", SemanticTokenTypes.string),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_strings_escape_interpolation2() async {
    var content = r'''
const value = 1;
const string1 = 'it\'s ${value} escaped\\\n';
''';

    var expected = [
      _Token('const', SemanticTokenTypes.keyword),
      _Token('value', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('1', SemanticTokenTypes.number),
      _Token('const', SemanticTokenTypes.keyword),
      _Token('string1', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token("'it", SemanticTokenTypes.string),
      _Token(r"\'", SemanticTokenTypes.string, [
        CustomSemanticTokenModifiers.escape,
      ]),
      _Token('s ', SemanticTokenTypes.string),
      _Token(r'${', CustomSemanticTokenTypes.source, [
        CustomSemanticTokenModifiers.interpolation,
      ]),
      _Token('value', SemanticTokenTypes.property),
      _Token('}', CustomSemanticTokenTypes.source, [
        CustomSemanticTokenModifiers.interpolation,
      ]),
      _Token(' escaped', SemanticTokenTypes.string),
      _Token(r'\\', SemanticTokenTypes.string, [
        CustomSemanticTokenModifiers.escape,
      ]),
      _Token(r'\n', SemanticTokenTypes.string, [
        CustomSemanticTokenModifiers.escape,
      ]),
      _Token(r"'", SemanticTokenTypes.string),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_topLevel() async {
    var content = '''
/// strings docs
const strings = <String>["test", 'test', r'test', \'''test\'''];

/// func docs
func(String a) => print(a);

/// abc docs
bool get abc => true;

final funcTearOff = func;

void f() {
  strings;
  func;
  abc;
  funcTearOff;
}
''';

    var expected = [
      _Token('/// strings docs', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token('const', SemanticTokenTypes.keyword),
      _Token('strings', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('String', SemanticTokenTypes.class_),
      _Token('"test"', SemanticTokenTypes.string),
      _Token("'test'", SemanticTokenTypes.string),
      _Token("r'test'", SemanticTokenTypes.string),
      _Token("'''test'''", SemanticTokenTypes.string),
      _Token('/// func docs', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token('func', SemanticTokenTypes.function, [
        SemanticTokenModifiers.declaration,
        SemanticTokenModifiers.static,
      ]),
      _Token('String', SemanticTokenTypes.class_),
      _Token('a', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('print', SemanticTokenTypes.function),
      _Token('a', SemanticTokenTypes.parameter),
      _Token('/// abc docs', SemanticTokenTypes.comment, [
        SemanticTokenModifiers.documentation,
      ]),
      _Token('bool', SemanticTokenTypes.class_),
      _Token('get', SemanticTokenTypes.keyword),
      _Token('abc', SemanticTokenTypes.property, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('true', CustomSemanticTokenTypes.boolean),
      _Token('final', SemanticTokenTypes.keyword),
      _Token('funcTearOff', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('func', SemanticTokenTypes.function),
      _Token('void', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.void_,
      ]),
      _Token('f', SemanticTokenTypes.function, [
        SemanticTokenModifiers.declaration,
        SemanticTokenModifiers.static,
      ]),
      _Token('strings', SemanticTokenTypes.property),
      _Token('func', SemanticTokenTypes.function),
      _Token('abc', SemanticTokenTypes.property),
      _Token('funcTearOff', SemanticTokenTypes.property),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_unresolvedOrInvalid() async {
    failTestOnErrorDiagnostic = false;

    // Unresolved/invalid names should be marked as "source", which is used to
    // mark up code the server thinks should be uncolored (without this, a
    // clients other grammars would show through, losing the benefit from having
    // resolved the code).
    var content = '''
void f() {
  int a;
  a.foo().bar.baz();

  dynamic b;
  b.foo().bar.baz();
}
''';

    var expected = [
      _Token('void', SemanticTokenTypes.keyword, [
        SemanticTokenModifiers('void'),
      ]),
      _Token('f', SemanticTokenTypes.function, [
        SemanticTokenModifiers.declaration,
        SemanticTokenModifiers.static,
      ]),
      _Token('int', SemanticTokenTypes.class_),
      _Token('a', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('a', SemanticTokenTypes.variable),
      _Token('foo', CustomSemanticTokenTypes.source),
      _Token('bar', CustomSemanticTokenTypes.source),
      _Token('baz', CustomSemanticTokenTypes.source),
      _Token('dynamic', SemanticTokenTypes.type),
      _Token('b', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('b', SemanticTokenTypes.variable),
      _Token('foo', CustomSemanticTokenTypes.source),
      _Token('bar', CustomSemanticTokenTypes.source),
      _Token('baz', CustomSemanticTokenTypes.source),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  Future<void> test_wildcard_forInVariable() async {
    var content = r'''
f() {
  for (var [!_!] in []) {}
}
''';

    var expected = [
      _Token('_', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.wildcard,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_wildcard_forLoopVariable() async {
    var content = r'''
f() {
  for (int [!_!] = 0;;) {}
}
''';

    var expected = [
      _Token('_', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.wildcard,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_wildcard_localVariable() async {
    var content = r'''
f() {
  var [!_!] = 1;
}
''';

    var expected = [
      _Token('_', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.wildcard,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_wildcard_notWildcard_memberName() async {
    var content = r'''
class A {
  void [!_!]() {}
}
''';

    var expected = [
      _Token('_', SemanticTokenTypes.method, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.instance,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_wildcard_notWildcard_parameter_constructor() async {
    var content = r'''
class A {
  A(String a);
}

class B extends A {
  String _;
  B([!this._, super._!]);
}
''';

    var expected = [
      _Token('this', SemanticTokenTypes.keyword),
      _Token('_', SemanticTokenTypes.variable, [
        CustomSemanticTokenModifiers.instance,
      ]),
      _Token('super', SemanticTokenTypes.keyword),
      _Token('_', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_wildcard_notWildcard_topLevelFunction() async {
    var content = r'''
void [!_!]() {}
''';

    var expected = [
      _Token('_', SemanticTokenTypes.function, [
        SemanticTokenModifiers.declaration,
        SemanticTokenModifiers.static,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_wildcard_notWildcard_topLevelVariable() async {
    var content = r'''
var [!_!] = 1;
''';

    var expected = [
      _Token('_', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_wildcard_notWildcard_typeName() async {
    var content = r'''
class [!_!] {}
''';

    var expected = [
      _Token('_', SemanticTokenTypes.class_, [
        SemanticTokenModifiers.declaration,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_wildcard_parameter_catchClause() async {
    var content = r'''
f() {
  try {
    throw '!';
  } catch ([!_, _!]) {
    print('oops');
  }
}
''';

    var expected = [
      _Token('_', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.wildcard,
      ]),
      _Token('_', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.wildcard,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_wildcard_parameter_functionExpression() async {
    var content = r'''
var a = [].where(([!_!]) => true);
''';

    var expected = [
      _Token('_', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.wildcard,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_wildcard_parameter_functionType() async {
    var content = r'''
typedef T = void Function(String [!_!]);
''';

    var expected = [
      _Token('_', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.wildcard,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_wildcard_parameter_instanceMethod() async {
    var content = r'''
class A {
  void f([!_!]) {}
}
''';

    var expected = [
      _Token('_', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.wildcard,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_wildcard_parameter_localFunction() async {
    var content = r'''
void f() {
  void f2([!_!]) {}
}
''';

    var expected = [
      _Token('_', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.wildcard,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_wildcard_parameter_staticMethod() async {
    var content = r'''
class A {
  static void f([!_!]) {}
}
''';

    var expected = [
      _Token('_', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.wildcard,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_wildcard_parameter_topLevelFunction() async {
    var content = r'''
void f([!_!]) {}
''';

    var expected = [
      _Token('_', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.wildcard,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_wildcard_pattern_assignment() async {
    var content = r'''
f() {
  int a;
([!_!], a) = (1, 2);
}
''';

    var expected = [
      _Token('_', SemanticTokenTypes.variable, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.wildcard,
      ]),
    ];

    await _initializeAndVerifyTokensInRange(content, expected);
  }

  Future<void> test_wildcard_typeParameter() async {
    var content = r'''
class T<_> {}
void genericFunction<_>(Object? _) {}
void f() {
  genericFunction(<_>() => true);
}
''';

    var expected = [
      _Token('class', SemanticTokenTypes.keyword),
      _Token('T', SemanticTokenTypes.class_, [
        SemanticTokenModifiers.declaration,
      ]),
      _Token('_', SemanticTokenTypes.typeParameter, [
        CustomSemanticTokenModifiers.wildcard,
      ]),
      _Token('void', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.void_,
      ]),
      _Token('genericFunction', SemanticTokenTypes.function, [
        SemanticTokenModifiers.declaration,
        SemanticTokenModifiers.static,
      ]),
      _Token('_', SemanticTokenTypes.typeParameter, [
        CustomSemanticTokenModifiers.wildcard,
      ]),
      _Token('Object', SemanticTokenTypes.class_),
      _Token('_', SemanticTokenTypes.parameter, [
        SemanticTokenModifiers.declaration,
        CustomSemanticTokenModifiers.wildcard,
      ]),
      _Token('void', SemanticTokenTypes.keyword, [
        CustomSemanticTokenModifiers.void_,
      ]),
      _Token('f', SemanticTokenTypes.function, [
        SemanticTokenModifiers.declaration,
        SemanticTokenModifiers.static,
      ]),
      _Token('genericFunction', SemanticTokenTypes.function),
      _Token('_', SemanticTokenTypes.typeParameter, [
        CustomSemanticTokenModifiers.wildcard,
      ]),
      _Token('true', CustomSemanticTokenTypes.boolean),
    ];

    await _initializeAndVerifyTokens(content, expected);
  }

  /// Decode tokens according to the LSP spec and pair with relevant file contents.
  List<_Token> _decodeSemanticTokens(String content, SemanticTokens tokens) {
    var contentLines = content.split(eol).map((line) => '$line$eol').toList();
    var results = <_Token>[];

    var lastLine = 0;
    var lastColumn = 0;
    for (var i = 0; i < tokens.data.length; i += 5) {
      var lineDelta = tokens.data[i];
      var columnDelta = tokens.data[i + 1];
      var length = tokens.data[i + 2];
      var tokenTypeIndex = tokens.data[i + 3];
      var modifierBitmask = tokens.data[i + 4];

      // Calculate the actual line/col from the deltas.
      var line = lastLine + lineDelta;
      var column = lineDelta == 0 ? lastColumn + columnDelta : columnDelta;

      var tokenContent = contentLines[line].substring(column, column + length);
      results.add(
        _Token(
          tokenContent,
          semanticTokenLegend.typeForIndex(tokenTypeIndex),
          semanticTokenLegend.modifiersForBitmask(modifierBitmask),
        ),
      );

      lastLine = line;
      lastColumn = column;
    }

    return results;
  }

  /// Initializes the server with [content] in [uri] and then calls
  /// [_verifyTokens] to check the semantic tokens match [expected].
  ///
  /// [content] will be normalized for the line endings being used for the test
  /// run.
  Future<void> _initializeAndVerifyTokens(
    String content,
    List<_Token> expected, {
    Uri? uri,
  }) async {
    uri ??= mainFileUri;
    content = normalizeNewlinesForPlatform(content);
    newFile(fromUri(uri), content);
    await initialize();

    await _verifyTokens(uri, content, expected);
  }

  /// Initializes the server with [content] in [uri] and then checks the
  ///  semantic tokens for the marked range match [expected].
  ///
  /// [content] will be normalized for the line endings being used for the test
  /// run.
  Future<void> _initializeAndVerifyTokensInRange(
    String content,
    List<_Token> expected, {
    Uri? uri,
  }) async {
    uri ??= mainFileUri;
    var code = TestCode.parseNormalized(content);
    newFile(fromUri(uri), code.code);
    await initialize();

    var tokens = await getSemanticTokensRange(mainFileUri, code.range.range);
    var decoded = _decodeSemanticTokens(code.code, tokens);
    expect(decoded, equals(expected));
  }

  /// Check the semantic tokens for [content] in [uri] match [expected].
  ///
  /// [content] is used to map the offsets in the response to the tokens and
  /// is not sent to the server, so it must already match what the server
  /// believes [uri] to contain.
  ///
  /// [content] will be normalized for the line endings being used for the test
  /// run.
  Future<void> _verifyTokens(
    Uri uri,
    String content,
    List<_Token> expected,
  ) async {
    content = normalizeNewlinesForPlatform(content);

    var tokens = await getSemanticTokens(uri);
    var decoded = _decodeSemanticTokens(content, tokens);
    expect(decoded, equals(expected));
  }
}

class _Token {
  final String content;
  final SemanticTokenTypes type;
  final List<SemanticTokenModifiers> modifiers;

  new(this.content, this.type, [this.modifiers = const []]);

  @override
  int get hashCode => content.hashCode;

  @override
  bool operator ==(Object o) =>
      o is _Token &&
      o.content == content &&
      o.type == type &&
      listEqual(
        // Treat nulls the same as empty lists for convenience when comparing.
        o.modifiers,
        modifiers,
        (SemanticTokenModifiers a, SemanticTokenModifiers b) => a == b,
      );

  /// Outputs a text representation of the token in the form of constructor
  /// args for easy copy/pasting into tests to update expectations.
  @override
  String toString() {
    var modifiersString = modifiers.isEmpty
        ? ''
        : ', [${modifiers.map((m) => 'SemanticTokenModifiers.$m').join(', ')}]';
    return "('$content', SemanticTokenTypes.$type$modifiersString)";
  }
}
