// Copyright (c) 2014, 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/protocol/protocol.dart';
import 'package:analysis_server/protocol/protocol_constants.dart';
import 'package:analysis_server/protocol/protocol_generated.dart';
import 'package:analyzer/source/line_info.dart';
import 'package:analyzer/source/source_range.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';
import 'package:collection/collection.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';

import '../analysis_server_base.dart';

void main() {
  defineReflectiveSuite(() {
    defineReflectiveTests(AnalysisNotificationHighlightsTest);
    defineReflectiveTests(HighlightTypeTest);
  });
}

@reflectiveTest
class AnalysisNotificationHighlightsTest extends HighlightsTestSupport {
  void assertHighlightText(TestCode testCode, int index, String expected) {
    var actual = _getHighlightText(testCode, index);
    if (actual != expected) {
      print(testCode.code.trimRight());
      print('-' * 32);
      print(actual);
    }
    expect(actual, expected);
  }

  Future<void> test_ANNOTATION_hasArguments() async {
    addTestFile('''
class AAA {
  const AAA(a, b, c);
}
@AAA(1, 2, 3) void f() {}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.ANNOTATION, '@AAA(', '@AAA('.length);
    assertHasRegion(HighlightRegionType.ANNOTATION, ') void f', ')'.length);
  }

  Future<void> test_ANNOTATION_hasTypeArguments_hasArguments() async {
    addTestFile('''
class AAA<T> {
  const AAA(a, b, c);
}

@AAA<int>(1, 2, 3) void f() {}
''');
    await prepareHighlights();
    assertHasRegion(
      HighlightRegionType.ANNOTATION,
      '@AAA',
      '@AAA<int>('.length,
    );
    assertHasRegion(HighlightRegionType.ANNOTATION, ') void', ')'.length);
    assertHasRegion(HighlightRegionType.CLASS, 'int>');
  }

  Future<void> test_ANNOTATION_noArguments() async {
    addTestFile('''
const AAA = 42;
@AAA void f() {}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.ANNOTATION, '@AAA');
  }

  Future<void> test_caseClause_case() async {
    addTestFile('''
void f(Object o) {
  if (o case 0) {}
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'case');
  }

  Future<void> test_castPattern_as() async {
    addTestFile('''
void f(Object o) {
  switch (o) {
    case var x as int:
      return;
  }
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'as int');
  }

  Future<void> test_CLASS() async {
    addTestFile('''
class AAA {}
AAA aaa;
Never nnn() => throw '';
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.CLASS, 'AAA {}');
    assertHasRegion(HighlightRegionType.CLASS, 'AAA aaa');
    assertHasRegion(HighlightRegionType.CLASS, 'Never nnn');
  }

  Future<void> test_class_constructor_factoryKeyword() async {
    var testCode = TestCode.parseNormalized(r'''
class A {
  A._();
[!
  factory () {}
  factory named() {}
!]
}
''');
    addTestFile(testCode.code);
    await prepareHighlights();
    assertHighlightText(testCode, 0, r'''
4:3 |factory| KEYWORD
5:3 |factory| KEYWORD
5:11 |named| CONSTRUCTOR
''');
  }

  Future<void> test_class_constructor_fieldFormalParameter() async {
    var testCode = TestCode.parseNormalized(r'''
class A {
  final int foo;
  A(this.foo);
}
''');
    addTestFile(testCode.code);
    await prepareHighlights();
    assertHighlightText(testCode, -1, r'''
1:1 |class| KEYWORD
1:7 |A| CLASS
2:3 |final| KEYWORD
2:9 |int| CLASS
2:13 |foo| INSTANCE_FIELD_DECLARATION
3:3 |A| CLASS
3:5 |this| KEYWORD
3:10 |foo| INSTANCE_FIELD_REFERENCE
''');
  }

  Future<void> test_class_constructor_newKeyword() async {
    var testCode = TestCode.parseNormalized(r'''
class A {
[!
  new () {}
  new named() {}
!]
}
''');
    addTestFile(testCode.code);
    await prepareHighlights();
    assertHighlightText(testCode, 0, r'''
3:3 |new| KEYWORD
4:3 |new| KEYWORD
4:7 |named| CONSTRUCTOR
''');
  }

  Future<void> test_class_constructor_primary_declaration() async {
    var testCode = TestCode.parseNormalized(r'''
class A {}
class B {}
mixin M {}

[!
class const D<T extends Object>.named(
  var int x, [
  final int y = 0,
]) extends A with M implements B {
  this {}
}
!]
''');
    addTestFile(testCode.code);
    await prepareHighlights();
    assertHighlightText(testCode, 0, r'''
6:1 |class| KEYWORD
6:7 |const| KEYWORD
6:13 |D| CLASS
6:15 |T| TYPE_PARAMETER
6:17 |extends| KEYWORD
6:25 |Object| CLASS
6:33 |named| CONSTRUCTOR
7:3 |var| KEYWORD
7:7 |int| CLASS
7:11 |x| PARAMETER_DECLARATION
8:3 |final| KEYWORD
8:9 |int| CLASS
8:13 |y| PARAMETER_DECLARATION
8:17 |0| LITERAL_INTEGER
9:4 |extends| KEYWORD
9:12 |A| CLASS
9:14 |with| KEYWORD
9:19 |M| MIXIN
9:21 |implements| KEYWORD
9:32 |B| CLASS
10:3 |this| KEYWORD
''');
  }

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

var a = [!A.named(1, b: 2);!]
''');
    addTestFile(testCode.code);
    await prepareHighlights();
    assertHighlightText(testCode, 0, r'''
3:9 |A| CONSTRUCTOR
3:11 |named| CONSTRUCTOR
3:17 |1| LITERAL_INTEGER
3:20 |b| PARAMETER_REFERENCE
3:23 |2| LITERAL_INTEGER
''');
  }

  Future<void> test_class_method_functionTypedFormalParameter() async {
    var testCode = TestCode.parseNormalized(r'''
class A {
  void foo(int bar(String a)) {}
}
''');
    addTestFile(testCode.code);
    await prepareHighlights();
    assertHighlightText(testCode, -1, r'''
1:1 |class| KEYWORD
1:7 |A| CLASS
2:3 |void| KEYWORD
2:8 |foo| INSTANCE_METHOD_DECLARATION
2:12 |int| CLASS
2:16 |bar| PARAMETER_DECLARATION
2:20 |String| CLASS
2:27 |a| PARAMETER_DECLARATION
''');
  }

  Future<void> test_CLASS_notDynamic() async {
    addTestFile('''
dynamic f() {}
''');
    await prepareHighlights();
    assertNoRegion(HighlightRegionType.CLASS, 'dynamic f()');
  }

  Future<void> test_CLASS_notVoid() async {
    addTestFile('''
void f() {}
''');
    await prepareHighlights();
    assertNoRegion(HighlightRegionType.CLASS, 'void f()');
  }

  Future<void> test_COMMENT() async {
    addTestFile('''
/*[0*//**
 * documentation comment
 *//*0]*/
void f() {
  /*[1*/// end-of-line comment/*1]*/
  my_function(1);
}

void my_function(String a) {
  /*[2*//* block comment *//*2]*/
}
''');
    await prepareHighlights();
    assertHasRange(HighlightRegionType.COMMENT_DOCUMENTATION, parsedRanges[0]);
    assertHasRange(HighlightRegionType.COMMENT_END_OF_LINE, parsedRanges[1]);
    assertHasRange(HighlightRegionType.COMMENT_BLOCK, parsedRanges[2]);
  }

  Future<void> test_constantPattern_const() async {
    addTestFile('''
void f(Object o) {
  switch (o) {
    case (const [0]):
      return;
  }
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'const');
  }

  Future<void> test_CONSTRUCTOR_explicitNew() async {
    addTestFile('''
class AAA<T> {
  AAA() {}
  AAA.name(p) {}
}
void f() {
  new AAA<int>();
  new AAA<int>.name(42);
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.CONSTRUCTOR, 'AAA<int>(');
    assertHasRegion(HighlightRegionType.CONSTRUCTOR, 'AAA<int>.name(');
    assertHasRegion(HighlightRegionType.CLASS, 'int>(');
    assertHasRegion(HighlightRegionType.CLASS, 'int>.name(');
    assertHasRegion(HighlightRegionType.CONSTRUCTOR, 'name(42)');
  }

  Future<void> test_CONSTRUCTOR_implicitNew() async {
    addTestFile('''
class AAA<T> {
  AAA() {}
  AAA.name(p) {}
}
void f() {
  AAA<int>();
  AAA<int>.name(42);
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.CONSTRUCTOR, 'AAA<int>(');
    assertHasRegion(HighlightRegionType.CONSTRUCTOR, 'AAA<int>.name(');
    assertHasRegion(HighlightRegionType.CLASS, 'int>(');
    assertHasRegion(HighlightRegionType.CLASS, 'int>.name(');
    assertHasRegion(HighlightRegionType.CONSTRUCTOR, 'name(p)');
    assertHasRegion(HighlightRegionType.CONSTRUCTOR, 'name(42)');
  }

  Future<void> test_CONSTRUCTOR_implicitNew_dotShorthand() async {
    addTestFile('''
class A {
  A();
  A.named(int x);
}
void f() {
  A a = .new();
  A aa = .named(42);
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.CONSTRUCTOR, 'new()');
    assertHasRegion(HighlightRegionType.CONSTRUCTOR, 'named(42)');
  }

  Future<void> test_CONSTRUCTOR_TEAR_OFF_dotShorthand() async {
    addTestFile('''
class A {
  A();
  A.named();
}
void f() {
  A a = .new;  // compile-time error
  A aa = .named; // compile-time error
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.CONSTRUCTOR_TEAR_OFF, 'new;');
    assertHasRegion(HighlightRegionType.CONSTRUCTOR_TEAR_OFF, 'named;');
  }

  Future<void> test_CONSTRUCTOR_TEAR_OFF_named() async {
    addTestFile('''
class A<T> {
  A.named();
}
void f() {
  A<int>.named;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.CLASS, 'A<int');
    assertHasRegion(HighlightRegionType.CLASS, 'int>');
    assertHasRegion(HighlightRegionType.CONSTRUCTOR_TEAR_OFF, 'named;');
  }

  Future<void> test_CONSTRUCTOR_TEAR_OFF_new_declared() async {
    addTestFile('''
class A<T> {
  A.new();
}
void f() {
  A<int>.new;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.CLASS, 'A<int');
    assertHasRegion(HighlightRegionType.CLASS, 'int>');
    assertHasRegion(HighlightRegionType.CONSTRUCTOR_TEAR_OFF, 'new;');
  }

  Future<void> test_CONSTRUCTOR_TEAR_OFF_new_synthetic() async {
    addTestFile('''
class A<T> {}
void f() {
  A<int>.new;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.CLASS, 'A<int');
    assertHasRegion(HighlightRegionType.CLASS, 'int>');
    assertHasRegion(HighlightRegionType.CONSTRUCTOR_TEAR_OFF, 'new;');
  }

  Future<void> test_declaredVariablePattern_final() async {
    addTestFile('''
void f(Object o) {
  switch (o) {
    case (final x as int):
      return;
  }
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'final');
  }

  Future<void> test_declaredVariablePattern_var() async {
    addTestFile('''
void f(Object o) {
  switch (o) {
    case (var x as int):
      return;
  }
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'var');
  }

  Future<void> test_DIRECTIVE() async {
    addTestFile('''
library lib;
import 'dart:math';
export 'dart:math';
part 'part.dart';
''');
    await prepareHighlights();
    assertHasStringRegion(HighlightRegionType.DIRECTIVE, 'library lib;');
    assertHasStringRegion(HighlightRegionType.DIRECTIVE, "import 'dart:math';");
    assertHasStringRegion(HighlightRegionType.DIRECTIVE, "export 'dart:math';");
    assertHasStringRegion(HighlightRegionType.DIRECTIVE, "part 'part.dart';");
  }

  Future<void> test_DIRECTIVE_configuration() async {
    addTestFile('''
import 'dart:math'
  if (dart.library.io) 'dart:io'
  if (dart.library.html) 'dart:html';
export 'dart:math'
  if (dart.library.io) 'dart:io'
  if (dart.library.html) 'dart:html';
''');
    await prepareHighlights();

    assertHasStringRegion(HighlightRegionType.DIRECTIVE, '''
import 'dart:math'
  if (dart.library.io) 'dart:io'
  if (dart.library.html) 'dart:html';''');

    assertHasStringRegion(HighlightRegionType.DIRECTIVE, '''
export 'dart:math'
  if (dart.library.io) 'dart:io'
  if (dart.library.html) 'dart:html';''');

    assertHasStringRegion(HighlightRegionType.KEYWORD, 'if');
    assertHasStringRegion(HighlightRegionType.LITERAL_STRING, "'dart:math'");
    assertHasStringRegion(HighlightRegionType.LITERAL_STRING, "'dart:io'");
    assertHasStringRegion(HighlightRegionType.LITERAL_STRING, "'dart:html'");
  }

  Future<void> test_DIRECTIVE_partOf() async {
    addTestFile('''
part of my.lib.name;
''');
    _addLibraryForTestPart();
    await prepareHighlights();
    assertHasStringRegion(
      HighlightRegionType.DIRECTIVE,
      'part of my.lib.name;',
    );
  }

  Future<void> test_DYNAMIC_LOCAL_VARIABLE() async {
    addTestFile('''
f() {}
void f(p) {
  var v = f();
  v;
}
''');
    await prepareHighlights();
    assertHasRegion(
      HighlightRegionType.DYNAMIC_LOCAL_VARIABLE_DECLARATION,
      'v = f()',
    );
    assertHasRegion(HighlightRegionType.DYNAMIC_LOCAL_VARIABLE_REFERENCE, 'v;');
  }

  Future<void> test_DYNAMIC_PARAMETER() async {
    addTestFile('''
void f(p) {
  print(p);
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.DYNAMIC_PARAMETER_DECLARATION, 'p)');
    assertHasRegion(HighlightRegionType.DYNAMIC_PARAMETER_REFERENCE, 'p);');
  }

  Future<void> test_DYNAMIC_VARIABLE_field() async {
    addTestFile('''
class A {
  var f;
  m() {
    f = 1;
  }
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.INSTANCE_FIELD_DECLARATION, 'f;');
    assertHasRegion(HighlightRegionType.INSTANCE_SETTER_REFERENCE, 'f = 1');
  }

  Future<void> test_enum_constant() async {
    var testCode = TestCode.parseNormalized(r'''
enum MyEnum {AAA, BBB}

void f() {
  MyEnum.AAA;
  MyEnum.BBB;
}
''');
    addTestFile(testCode.code);
    await prepareHighlights();
    assertHighlightText(testCode, -1, r'''
1:1 |enum| KEYWORD
1:6 |MyEnum| ENUM
1:14 |AAA| ENUM_CONSTANT
1:19 |BBB| ENUM_CONSTANT
3:1 |void| KEYWORD
3:6 |f| TOP_LEVEL_FUNCTION_DECLARATION
4:3 |MyEnum| ENUM
4:10 |AAA| ENUM_CONSTANT
5:3 |MyEnum| ENUM
5:10 |BBB| ENUM_CONSTANT
''');
  }

  Future<void> test_enum_constant_dotShorthand() async {
    addTestFile('''
enum E { a }
void f() {
  E e = .a;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.ENUM_CONSTANT, 'a;');
  }

  Future<void> test_enum_constructor() async {
    var testCode = TestCode.parseNormalized(r'''
const a = 0;

enum E<T> {
  [!v<int>.named(a);
  E.named(T a);!]
}
''');
    addTestFile(testCode.code);
    await prepareHighlights();
    assertHighlightText(testCode, 0, r'''
4:3 |v| ENUM_CONSTANT
4:5 |int| CLASS
4:10 |named| CONSTRUCTOR
4:16 |a| TOP_LEVEL_GETTER_REFERENCE
5:3 |E| ENUM
5:5 |named| CONSTRUCTOR
5:11 |T| TYPE_PARAMETER
5:13 |a| PARAMETER_DECLARATION
''');
  }

  Future<void> test_enum_field_instance() async {
    addTestFile('''
enum E {
  v;
  final int a = 0;
  E(this.a);
}

void f(E e) {
  e.a;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.CLASS, 'int ');
    assertHasRegion(HighlightRegionType.INSTANCE_FIELD_DECLARATION, 'a = 0');
    assertHasRegion(HighlightRegionType.INSTANCE_FIELD_REFERENCE, 'a);');
    assertHasRegion(HighlightRegionType.INSTANCE_GETTER_REFERENCE, 'a;');
  }

  Future<void> test_enum_field_static() async {
    addTestFile('''
enum E {
  v;
  static final int a = 0;
}

void f() {
  E.a;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.CLASS, 'int ');
    assertHasRegion(HighlightRegionType.STATIC_FIELD_DECLARATION, 'a = 0');
    assertHasRegion(HighlightRegionType.STATIC_GETTER_REFERENCE, 'a;');
  }

  Future<void> test_enum_getter_instance() async {
    addTestFile('''
enum E {
  v;
  int get foo => 0;
}

void f(E e) {
  e.foo;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.CLASS, 'int get');
    assertHasRegion(HighlightRegionType.INSTANCE_GETTER_DECLARATION, 'foo =>');
    assertHasRegion(HighlightRegionType.INSTANCE_GETTER_REFERENCE, 'foo;');
  }

  Future<void> test_enum_getter_static() async {
    addTestFile('''
enum E {
  v;
  static int get foo => 0;
}

void f() {
  E.foo;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.CLASS, 'int get');
    assertHasRegion(HighlightRegionType.STATIC_GETTER_DECLARATION, 'foo =>');
    assertHasRegion(HighlightRegionType.STATIC_GETTER_REFERENCE, 'foo;');
  }

  Future<void> test_enum_method_instance() async {
    addTestFile('''
enum E {
  v;
  int foo(int a) {
    return a;
  }
}

void f(E e) {
  e.foo();
  e.foo;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.CLASS, 'int foo');
    assertHasRegion(HighlightRegionType.INSTANCE_METHOD_DECLARATION, 'foo(int');
    assertHasRegion(HighlightRegionType.CLASS, 'int a');
    assertHasRegion(HighlightRegionType.PARAMETER_DECLARATION, 'a)');
    assertHasRegion(HighlightRegionType.PARAMETER_REFERENCE, 'a;');
    assertHasRegion(HighlightRegionType.INSTANCE_METHOD_REFERENCE, 'foo();');
    assertHasRegion(HighlightRegionType.INSTANCE_METHOD_TEAR_OFF, 'foo;');
  }

  Future<void> test_enum_method_static() async {
    addTestFile('''
enum E {
  v;
  static int foo(int a) {
    return a;
  }
}

void f() {
  E.foo();
  E.foo;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.CLASS, 'int foo');
    assertHasRegion(HighlightRegionType.STATIC_METHOD_DECLARATION, 'foo(int');
    assertHasRegion(HighlightRegionType.CLASS, 'int a');
    assertHasRegion(HighlightRegionType.PARAMETER_DECLARATION, 'a)');
    assertHasRegion(HighlightRegionType.PARAMETER_REFERENCE, 'a;');
    assertHasRegion(HighlightRegionType.STATIC_METHOD_REFERENCE, 'foo();');
    assertHasRegion(HighlightRegionType.STATIC_METHOD_TEAR_OFF, 'foo;');
  }

  Future<void> test_enum_name() async {
    addTestFile('''
enum MyEnum {A, B, C}

MyEnum value;
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.ENUM, 'MyEnum {');
    assertHasRegion(HighlightRegionType.ENUM, 'MyEnum value;');
  }

  Future<void> test_enum_setter_instance() async {
    addTestFile('''
enum E {
  v;
  set foo(int _) {}
}

void f(E e) {
  e.foo = 0;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.CLASS, 'int _');
    assertHasRegion(HighlightRegionType.INSTANCE_SETTER_DECLARATION, 'foo(int');
    assertHasRegion(HighlightRegionType.INSTANCE_SETTER_REFERENCE, 'foo = 0;');
  }

  Future<void> test_enum_setter_static() async {
    addTestFile('''
enum E {
  v;
  static set foo(int _) {}
}

void f() {
  E.foo = 0;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.CLASS, 'int _');
    assertHasRegion(HighlightRegionType.STATIC_SETTER_DECLARATION, 'foo(int');
    assertHasRegion(HighlightRegionType.STATIC_SETTER_REFERENCE, 'foo = 0;');
  }

  Future<void> test_enum_typeParameter() async {
    addTestFile('''
enum E<T> {
  v;
  T? foo() => null;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.TYPE_PARAMETER, 'T>');
    assertHasRegion(HighlightRegionType.TYPE_PARAMETER, 'T?');
  }

  Future<void> test_EXTENSION() async {
    addTestFile('''
extension E on int {
  void foo() {}
  static void bar() {}
}
void f() {
  E(0).foo();
  E.bar();
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.EXTENSION, 'E on int');
    assertHasRegion(HighlightRegionType.EXTENSION, 'E(0)');
    assertHasRegion(HighlightRegionType.EXTENSION, 'E.bar()');
  }

  Future<void> test_extension_augment() async {
    var testCode = TestCode.parseNormalized(r'''
augment extension E {}
''');
    addTestFile(testCode.code);
    await prepareHighlights();
    assertHighlightText(testCode, -1, r'''
1:1 |augment| KEYWORD
1:9 |extension| KEYWORD
1:19 |E| EXTENSION
''');
  }

  Future<void> test_extension_augment_hasOnClause() async {
    var testCode = TestCode.parseNormalized(r'''
augment extension E on int {}
''');
    addTestFile(testCode.code);
    await prepareHighlights();
    assertHighlightText(testCode, -1, r'''
1:1 |augment| KEYWORD
1:9 |extension| KEYWORD
1:19 |E| EXTENSION
1:21 |on| KEYWORD
1:24 |int| CLASS
''');
  }

  Future<void> test_extensionType() async {
    var testCode = TestCode.parseNormalized(r'''
extension type const A<T>.named(int it) implements num {}
''');
    addTestFile(testCode.code);
    await prepareHighlights();
    assertHighlightText(testCode, -1, r'''
1:1 |extension| KEYWORD
1:11 |type| KEYWORD
1:16 |const| KEYWORD
1:22 |A| EXTENSION_TYPE
1:24 |T| TYPE_PARAMETER
1:27 |named| CONSTRUCTOR
1:33 |int| CLASS
1:37 |it| PARAMETER_DECLARATION
1:41 |implements| KEYWORD
1:52 |num| CLASS
''');
  }

  Future<void> test_forEachPartsWithPattern_final() async {
    addTestFile('''
void f(List<Object> l) {
  for (final i as int in l) {}
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'final');
  }

  Future<void> test_forEachPartsWithPattern_var() async {
    addTestFile('''
void f(List<Object> l) {
  for (var i as int in l) {}
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'var');
  }

  Future<void> test_forPartsWithDeclarations() async {
    addTestFile('''
void f() {
  for (var i = 0; i < 10; i++) {}
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.LOCAL_VARIABLE_DECLARATION, 'i = 0');
    assertHasRegion(HighlightRegionType.LOCAL_VARIABLE_REFERENCE, 'i < 10');
    assertHasRegion(HighlightRegionType.LOCAL_VARIABLE_REFERENCE, 'i++');
  }

  Future<void> test_forPartsWithDeclarations_dynamic() async {
    addTestFile('''
void f() {
  for (dynamic i = 0; i < 10; i++) {}
}
''');
    await prepareHighlights();
    assertHasRegion(
      HighlightRegionType.DYNAMIC_LOCAL_VARIABLE_DECLARATION,
      'i = 0',
    );
    assertHasRegion(
      HighlightRegionType.DYNAMIC_LOCAL_VARIABLE_REFERENCE,
      'i < 10',
    );
    assertHasRegion(
      HighlightRegionType.DYNAMIC_LOCAL_VARIABLE_REFERENCE,
      'i++',
    );
  }

  Future<void> test_FUNCTION_TYPE_ALIAS() async {
    addTestFile('''
typedef A();
typedef B = void Function();
void f(A a, B b) {}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.FUNCTION_TYPE_ALIAS, 'A();');
    assertHasRegion(HighlightRegionType.FUNCTION_TYPE_ALIAS, 'A a');
    assertHasRegion(HighlightRegionType.FUNCTION_TYPE_ALIAS, 'B = ');
    assertHasRegion(HighlightRegionType.FUNCTION_TYPE_ALIAS, 'B b');
  }

  Future<void> test_GETTER() async {
    addTestFile('''
get aaa => null;
class A {
  get bbb => null;
  static get ccc => null;
}
void f(A a) {
  aaa;
  a.bbb;
  A.ccc;
}
''');
    await prepareHighlights();
    assertHasRegion(
      HighlightRegionType.TOP_LEVEL_GETTER_DECLARATION,
      'aaa => null',
    );
    assertHasRegion(
      HighlightRegionType.INSTANCE_GETTER_DECLARATION,
      'bbb => null',
    );
    assertHasRegion(
      HighlightRegionType.STATIC_GETTER_DECLARATION,
      'ccc => null',
    );
    assertHasRegion(HighlightRegionType.TOP_LEVEL_GETTER_REFERENCE, 'aaa;');
    assertHasRegion(HighlightRegionType.INSTANCE_GETTER_REFERENCE, 'bbb;');
    assertHasRegion(HighlightRegionType.STATIC_GETTER_REFERENCE, 'ccc;');
  }

  Future<void> test_GETTER_dotShorthand() async {
    addTestFile('''
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() {
  A a = .aGetter;
  B b = .bGetter;
  Mixin m = .dGetter;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.STATIC_GETTER_REFERENCE, 'aGetter;');
    assertHasRegion(HighlightRegionType.STATIC_GETTER_REFERENCE, 'bGetter;');
    assertHasRegion(HighlightRegionType.STATIC_GETTER_REFERENCE, 'dGetter;');
  }

  Future<void> test_IDENTIFIER_DEFAULT() async {
    addTestFile('''
void f() {
  aaa = 42;
  bbb(84);
  CCC ccc;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.IDENTIFIER_DEFAULT, 'aaa = 42');
    assertHasRegion(HighlightRegionType.IDENTIFIER_DEFAULT, 'bbb(84)');
    assertHasRegion(HighlightRegionType.IDENTIFIER_DEFAULT, 'CCC ccc');
  }

  Future<void> test_IMPORT_PREFIX() async {
    addTestFile('''
import 'dart:math' as ma;
void f() {
  ma.max(1, 2);
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.IMPORT_PREFIX, 'ma;');
    assertHasRegion(HighlightRegionType.IMPORT_PREFIX, 'ma.max');
  }

  Future<void> test_IMPORT_PREFIX_methodInvocation() async {
    addTestFile('''
import 'dart:math' as ma;
void f() {
  ma.max(1, 2);
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.IMPORT_PREFIX, 'ma;');
    assertHasRegion(HighlightRegionType.IMPORT_PREFIX, 'ma.max');
  }

  Future<void> test_IMPORT_PREFIX_namedType() async {
    addTestFile('''
import 'dart:math' as math;
void f(math.Random r) {}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.IMPORT_PREFIX, 'math;');
    assertHasRegion(HighlightRegionType.IMPORT_PREFIX, 'math.Random');
  }

  Future<void> test_INSTANCE_FIELD() async {
    addTestFile('''
class A {
  int aaa = 1;
  int bbb = 2;
  A([this.bbb = 3]);
}
void f(A a) {
  a.aaa = 4;
  a.bbb = 5;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.INSTANCE_FIELD_DECLARATION, 'aaa = 1');
    assertHasRegion(HighlightRegionType.INSTANCE_FIELD_DECLARATION, 'bbb = 2');
    assertHasRegion(HighlightRegionType.INSTANCE_FIELD_REFERENCE, 'bbb = 3');
    assertHasRegion(HighlightRegionType.INSTANCE_SETTER_REFERENCE, 'aaa = 4');
    assertHasRegion(HighlightRegionType.INSTANCE_SETTER_REFERENCE, 'bbb = 5');
  }

  Future<void> test_INSTANCE_FIELD_dynamic() async {
    addTestFile('''
class A {
  var f;
  A(this.f);
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.INSTANCE_FIELD_DECLARATION, 'f;');
    assertHasRegion(HighlightRegionType.INSTANCE_FIELD_REFERENCE, 'f);');
  }

  Future<void> test_instanceCreation_class() async {
    var testCode = TestCode.parseNormalized(r'''
class A {
  A.named(int it);
}
void f() {
  [!A.named(0)!];
}
''');
    addTestFile(testCode.code);
    await prepareHighlights();
    assertHighlightText(testCode, 0, r'''
5:3 |A| CONSTRUCTOR
5:5 |named| CONSTRUCTOR
5:11 |0| LITERAL_INTEGER
''');
  }

  Future<void> test_instanceCreation_extensionType() async {
    var testCode = TestCode.parseNormalized(r'''
extension type A.named(T it) {}
void f() {
  [!A.named(0)!];
}
''');
    addTestFile(testCode.code);
    await prepareHighlights();
    assertHighlightText(testCode, 0, r'''
3:3 |A| CONSTRUCTOR
3:5 |named| CONSTRUCTOR
3:11 |0| LITERAL_INTEGER
''');
  }

  Future<void> test_KEYWORD() async {
    addTestFile('''
void f() {
  assert(true);
  for (;;) break;
  switch (0) {
    case 0: break;
    default: break;
  }
  try {} catch (e) {}
  const v1 = 0;
  for (;;) continue;
  do {} while (true);
  if (true) {} else {}
  var v2 = false;
  final v3 = 1;
  try {} finally {}
  for (var v4 in []) {}
  v3 is int;
  new A();
  try {} catch (e) {rethrow;}
  var v5 = true;
  while (true) {}
}
class A {}
class B extends A {
  B() : super();
  m() {
    return this;
  }
}
class C = Object with A;
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'assert(true)');
    assertHasRegion(HighlightRegionType.KEYWORD, 'for (;;)');
    assertHasRegion(HighlightRegionType.KEYWORD, 'for (var v4 in');
    assertHasRegion(HighlightRegionType.KEYWORD, 'var v4 in');
    assertHasRegion(HighlightRegionType.KEYWORD, 'break;');
    assertHasRegion(HighlightRegionType.KEYWORD, 'case 0:');
    assertHasRegion(HighlightRegionType.KEYWORD, 'catch (e) {}');
    assertHasRegion(HighlightRegionType.KEYWORD, 'class A {}');
    assertHasRegion(HighlightRegionType.KEYWORD, 'const v1');
    assertHasRegion(HighlightRegionType.KEYWORD, 'continue;');
    assertHasRegion(HighlightRegionType.KEYWORD, 'default:');
    assertHasRegion(HighlightRegionType.KEYWORD, 'do {} while');
    assertHasRegion(HighlightRegionType.KEYWORD, 'if (true)');
    assertHasRegion(HighlightRegionType.KEYWORD, 'false;');
    assertHasRegion(HighlightRegionType.KEYWORD, 'final v3 =');
    assertHasRegion(HighlightRegionType.KEYWORD, 'finally {}');
    assertHasRegion(HighlightRegionType.KEYWORD, 'in []');
    assertHasRegion(HighlightRegionType.KEYWORD, 'is int');
    assertHasRegion(HighlightRegionType.KEYWORD, 'new A();');
    assertHasRegion(HighlightRegionType.KEYWORD, 'rethrow;');
    assertHasRegion(HighlightRegionType.KEYWORD, 'return this');
    assertHasRegion(HighlightRegionType.KEYWORD, 'super();');
    assertHasRegion(HighlightRegionType.KEYWORD, 'switch (0)');
    assertHasRegion(HighlightRegionType.KEYWORD, 'this;');
    assertHasRegion(HighlightRegionType.KEYWORD, 'true;');
    assertHasRegion(HighlightRegionType.KEYWORD, 'try {');
    assertHasRegion(HighlightRegionType.KEYWORD, 'while (true) {}');
    assertHasRegion(HighlightRegionType.KEYWORD, 'while (true);');
    assertHasRegion(HighlightRegionType.KEYWORD, 'with A;');
  }

  Future<void> test_KEYWORD_abstract() async {
    addTestFile('''
abstract class A {};
abstract class B = Object with A;
void f() {
  var abstract = 42;
}''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'abstract class A');
    assertHasRegion(HighlightRegionType.KEYWORD, 'abstract class B');
    assertNoRegion(HighlightRegionType.KEYWORD, 'abstract = 42');
  }

  Future<void> test_KEYWORD_as() async {
    addTestFile('''
import 'dart:math' as math;
void f() {
  p as int;
  var as = 42;
}''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'as math');
    assertHasRegion(HighlightRegionType.KEYWORD, 'as int');
    assertNoRegion(HighlightRegionType.KEYWORD, 'as = 42');
  }

  Future<void> test_KEYWORD_async() async {
    addTestFile('''
fa() async {}
fb() async* {}
void f() {
  bool async = false;
}
''');
    await prepareHighlights();
    assertHasStringRegion(HighlightRegionType.KEYWORD, 'async');
    assertHasStringRegion(HighlightRegionType.KEYWORD, 'async*');
    assertNoRegion(HighlightRegionType.KEYWORD, 'async = false');
  }

  Future<void> test_KEYWORD_augment_onClass() async {
    var testCode = TestCode.parseNormalized(r'''
augment class A {}
''');
    addTestFile(testCode.code);
    await prepareHighlights();
    assertHighlightText(testCode, -1, r'''
1:1 |augment| KEYWORD
1:9 |class| KEYWORD
1:15 |A| CLASS
''');
  }

  Future<void> test_KEYWORD_augment_onConstructor() async {
    addTestFile('''
class C {
  augment C() {}
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'augment');
  }

  @SkippedTest(reason: 'The token is not supported')
  Future<void> test_KEYWORD_augment_onEnum() async {
    addTestFile('''
augment enum E {a, b}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'augment');
  }

  Future<void> test_KEYWORD_augment_onInstanceGetter() async {
    addTestFile('''
class C {
  augment int get g => 0;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'augment');
  }

  Future<void> test_KEYWORD_augment_onInstanceMethod() async {
    addTestFile('''
class C {
  augment int m() => 0;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'augment');
  }

  Future<void> test_KEYWORD_augment_onInstanceSetter() async {
    addTestFile('''
class C {
  augment set s(int x) {}
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'augment');
  }

  Future<void> test_KEYWORD_augment_onMixin() async {
    addTestFile('''
augment mixin M {}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'augment');
  }

  Future<void> test_KEYWORD_augment_onOperator() async {
    addTestFile('''
class C {
  augment int operator -() => 0;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'augment');
  }

  Future<void> test_KEYWORD_augment_onStaticMethod() async {
    addTestFile('''
class C {
  augment static int m() => 0;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'augment');
  }

  Future<void> test_KEYWORD_augment_onTopLevelFunction() async {
    addTestFile('''
augment int f(int x) => 0;
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'augment');
  }

  Future<void> test_KEYWORD_augment_onTopLevelGetter() async {
    addTestFile('''
augment int get g => 0;
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'augment');
  }

  Future<void> test_KEYWORD_augment_onTopLevelSetter() async {
    addTestFile('''
augment set s(int x) {}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'augment');
  }

  Future<void> test_KEYWORD_augment_onTopLevelVariable() async {
    addTestFile('''
augment int v = 0;
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'augment');
  }

  @SkippedTest() // TODO(scheglov): implement augmentation
  Future<void> test_KEYWORD_augmented_onInstanceGetter() async {
    addTestFile('''
class C {
  augment int get g => augmented;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'augmented');
  }

  @SkippedTest() // TODO(scheglov): implement augmentation
  Future<void> test_KEYWORD_augmented_onInstanceMethod() async {
    addTestFile('''
class C {
  augment int m() => augmented();
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'augmented');
  }

  @SkippedTest() // TODO(scheglov): implement augmentation
  Future<void> test_KEYWORD_augmented_onInstanceSetter() async {
    addTestFile('''
class C {
  augment set s(int x) { augmented = x; }
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'augmented');
  }

  @SkippedTest() // TODO(scheglov): implement augmentation
  Future<void> test_KEYWORD_augmented_onStaticMethod() async {
    addTestFile('''
class C {
  augment static int m() => augmented();
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'augmented');
  }

  @SkippedTest() // TODO(scheglov): implement augmentation
  Future<void> test_KEYWORD_augmented_onTopLevelFunction() async {
    addTestFile('''
augment int f(int x) => augmented(x);
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'augmented');
  }

  @SkippedTest() // TODO(scheglov): implement augmentation
  Future<void> test_KEYWORD_augmented_onTopLevelGetter() async {
    addTestFile('''
augment int get g => augmented;
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'augmented');
  }

  @SkippedTest() // TODO(scheglov): implement augmentation
  Future<void> test_KEYWORD_augmented_onTopLevelSetter() async {
    addTestFile('''
augment set s(int x) { augmented = x; }
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'augmented');
  }

  Future<void> test_KEYWORD_await() async {
    addTestFile('''
void f() async {
  await 42;
  await for (var item in []) {
    print(item);
  }
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'await 42');
    assertHasRegion(HighlightRegionType.KEYWORD, 'await for');
  }

  Future<void> test_KEYWORD_awaitForIn_list() async {
    addTestFile('''
f(a) async {
  return [await for(var b in a) b];
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'await');
    assertHasRegion(HighlightRegionType.KEYWORD, 'for');
    assertHasRegion(HighlightRegionType.KEYWORD, 'var');
    assertHasRegion(HighlightRegionType.KEYWORD, 'in');
  }

  Future<void> test_KEYWORD_awaitForIn_map() async {
    addTestFile('''
f(a) async {
  return {await for(var b in a) b : 0};
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'await');
    assertHasRegion(HighlightRegionType.KEYWORD, 'for');
    assertHasRegion(HighlightRegionType.KEYWORD, 'var');
    assertHasRegion(HighlightRegionType.KEYWORD, 'in');
  }

  Future<void> test_KEYWORD_awaitForIn_set() async {
    addTestFile('''
f(a) async {
  return {await for(var b in a) b};
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'await');
    assertHasRegion(HighlightRegionType.KEYWORD, 'for');
    assertHasRegion(HighlightRegionType.KEYWORD, 'var');
    assertHasRegion(HighlightRegionType.KEYWORD, 'in');
  }

  Future<void> test_KEYWORD_base() async {
    addTestFile('''
base class A {}
base mixin M {}
base class B = Object with M;
void f() {
  var base = 42;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'base class A');
    assertHasRegion(HighlightRegionType.KEYWORD, 'base mixin M');
    assertHasRegion(HighlightRegionType.KEYWORD, 'base class B');
    assertNoRegion(HighlightRegionType.KEYWORD, 'base = 42');
  }

  Future<void> test_KEYWORD_const_constructor() async {
    addTestFile('''
class A {
  const A(); // 1
}
const a = const A(); // 2
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'const A(); // 1');
    assertHasRegion(HighlightRegionType.KEYWORD, 'const a =');
    assertHasRegion(HighlightRegionType.KEYWORD, 'const A(); // 2');
  }

  Future<void> test_KEYWORD_const_list() async {
    addTestFile('''
var v = const [];
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'const');
  }

  Future<void> test_KEYWORD_const_map() async {
    addTestFile('''
var v = const {0 : 1};
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'const');
  }

  Future<void> test_KEYWORD_const_set() async {
    addTestFile('''
var v = const {0};
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'const');
  }

  Future<void> test_KEYWORD_covariant() async {
    addTestFile('''
void f(covariant Object object) {}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'covariant');
  }

  Future<void> test_KEYWORD_deferred() async {
    addTestFile('''
import 'dart:math' deferred as math;
void f() {
  var deferred = 42;
}''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'deferred as math');
    assertNoRegion(HighlightRegionType.KEYWORD, 'deferred = 42');
  }

  Future<void> test_KEYWORD_export() async {
    addTestFile('''
export "dart:math";
void f() {
  var export = 42;
}''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'export "dart:');
    assertNoRegion(HighlightRegionType.KEYWORD, 'export = 42');
  }

  Future<void> test_KEYWORD_external() async {
    addTestFile('''
class A {
  external A();
  external aaa();
}
external f() {
  var external = 42;
}''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'external A()');
    assertHasRegion(HighlightRegionType.KEYWORD, 'external aaa()');
    assertHasRegion(HighlightRegionType.KEYWORD, 'external f()');
    assertNoRegion(HighlightRegionType.KEYWORD, 'external = 42');
  }

  Future<void> test_KEYWORD_factory() async {
    addTestFile('''
class A {
  A.named();
  factory A() => A.named();
}
void f() {
  var factory = 42;
}''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'factory A()');
    assertNoRegion(HighlightRegionType.KEYWORD, 'factory = 42');
  }

  Future<void> test_KEYWORD_final() async {
    addTestFile('''
final class A {}
final class B = Object with M;
void f() {
  var final = 42;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'final class A');
    assertHasRegion(HighlightRegionType.KEYWORD, 'final class B');
    assertNoRegion(HighlightRegionType.KEYWORD, 'final = 42');
  }

  Future<void> test_KEYWORD_Function() async {
    addTestFile('''
typedef F = void Function();

void f(void Function() a, Function b) {}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'Function();');
    assertHasRegion(HighlightRegionType.KEYWORD, 'Function() a');
    assertNoRegion(HighlightRegionType.KEYWORD, 'Function b');
  }

  Future<void> test_KEYWORD_get() async {
    addTestFile('''
get aaa => 1;
class A {
  get bbb => 2;
}
void f() {
  var get = 42;
}''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'get aaa =>');
    assertHasRegion(HighlightRegionType.KEYWORD, 'get bbb =>');
    assertNoRegion(HighlightRegionType.KEYWORD, 'get = 42');
  }

  Future<void> test_KEYWORD_hide() async {
    addTestFile('''
import 'foo.dart' hide Foo;
void f() {
  var hide = 42;
}''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'hide Foo');
    assertNoRegion(HighlightRegionType.KEYWORD, 'hide = 42');
  }

  Future<void> test_KEYWORD_if_list() async {
    addTestFile('''
f(a, b) {
  return [if (a < b) 'a'];
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'if');
  }

  Future<void> test_KEYWORD_if_map() async {
    addTestFile('''
f(a, b) {
  return {if (a < b) 'a' : 1};
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'if');
  }

  Future<void> test_KEYWORD_if_set() async {
    addTestFile('''
f(a, b) {
  return {if (a < b) 'a'};
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'if');
  }

  Future<void> test_KEYWORD_ifElse_list() async {
    addTestFile('''
f(a, b) {
  return [if (a < b) 'a' else 'b'];
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'if');
    assertHasRegion(HighlightRegionType.KEYWORD, 'else');
  }

  Future<void> test_KEYWORD_ifElse_map() async {
    addTestFile('''
f(a, b) {
  return {if (a < b) 'a' : 1 else 'b' : 2};
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'if');
    assertHasRegion(HighlightRegionType.KEYWORD, 'else');
  }

  Future<void> test_KEYWORD_ifElse_set() async {
    addTestFile('''
f(a, b) {
  return {if (a < b) 'a' else 'b'};
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'if');
    assertHasRegion(HighlightRegionType.KEYWORD, 'else');
  }

  Future<void> test_KEYWORD_ifElse_statement() async {
    addTestFile('''
f(a, b) {
  if (a < b) {} else {}
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'if');
    assertHasRegion(HighlightRegionType.KEYWORD, 'else');
  }

  Future<void> test_KEYWORD_implements() async {
    addTestFile('''
class A {}
class B implements A {}
void f() {
  var implements = 42;
}''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'implements A {}');
    assertNoRegion(HighlightRegionType.KEYWORD, 'implements = 42');
  }

  Future<void> test_KEYWORD_import() async {
    addTestFile('''
import "foo.dart";
void f() {
  var import = 42;
}''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'import "');
    assertNoRegion(HighlightRegionType.KEYWORD, 'import = 42');
  }

  Future<void> test_KEYWORD_interface() async {
    addTestFile('''
interface class A {}
interface class B = Object with M;
void f() {
  var interface = 42;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'interface class A');
    assertHasRegion(HighlightRegionType.KEYWORD, 'interface class B');
    assertNoRegion(HighlightRegionType.KEYWORD, 'interface = 42');
  }

  Future<void> test_KEYWORD_late() async {
    addTestFile('''
class C {
  late int x;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'late');
  }

  Future<void> test_KEYWORD_library() async {
    addTestFile('''
library lib;
void f() {
  var library = 42;
}''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'library lib;');
    assertNoRegion(HighlightRegionType.KEYWORD, 'library = 42');
  }

  Future<void> test_KEYWORD_mixin() async {
    addTestFile('''
mixin class A {}
mixin M {}
mixin class B = Object with M;
void f() {
  var mixin = 42;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'mixin class A');
    assertHasRegion(HighlightRegionType.KEYWORD, 'mixin class B');
    assertNoRegion(HighlightRegionType.KEYWORD, 'mixin = 42');
  }

  Future<void> test_KEYWORD_native() async {
    addTestFile('''
class A native "A_native" {}
class B {
  bbb() native "bbb_native";
}
void f() {
  var native = 42;
}''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'native "A_');
    assertHasRegion(HighlightRegionType.KEYWORD, 'native "bbb_');
    assertNoRegion(HighlightRegionType.KEYWORD, 'native = 42');
  }

  Future<void> test_KEYWORD_on_inMixin() async {
    addTestFile('''
mixin M on N {}
class N {}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'on N');
  }

  Future<void> test_KEYWORD_on_inTry() async {
    addTestFile('''
void f() {
  try {
  } on int catch (e) {
  }
  var on = 42;
}''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'on int');
    assertNoRegion(HighlightRegionType.KEYWORD, 'on = 42');
  }

  Future<void> test_KEYWORD_operator() async {
    addTestFile('''
class A {
  operator +(x) => null;
}
void f() {
  var operator = 42;
}''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'operator +(');
    assertNoRegion(HighlightRegionType.KEYWORD, 'operator = 42');
  }

  Future<void> test_KEYWORD_part() async {
    addTestFile('''
part "my_part.dart";
void f() {
  var part = 42;
}''');
    newFile('/project/bin/my_part.dart', 'part of lib;');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'part "my_');
    assertNoRegion(HighlightRegionType.KEYWORD, 'part = 42');
  }

  Future<void> test_KEYWORD_partOf() async {
    addTestFile('''
part of my.lib.name;
void f() {
  var part = 1;
  var of = 2;
}''');
    _addLibraryForTestPart();
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'part of', 'part of'.length);
    assertNoRegion(HighlightRegionType.KEYWORD, 'part = 1');
    assertNoRegion(HighlightRegionType.KEYWORD, 'of = 2');
  }

  Future<void> test_KEYWORD_required() async {
    addTestFile('''
void f({required int x}) {}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'required');
  }

  Future<void> test_KEYWORD_sealed() async {
    addTestFile('''
sealed class A {}
sealed class B = Object with M;
void f() {
  var sealed = 42;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'sealed class A');
    assertHasRegion(HighlightRegionType.KEYWORD, 'sealed class B');
    assertNoRegion(HighlightRegionType.KEYWORD, 'sealed = 42');
  }

  Future<void> test_KEYWORD_set() async {
    addTestFile('''
set aaa(x) {}
class A
  set bbb(x) {}
}
void f() {
  var set = 42;
}''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'set aaa(');
    assertHasRegion(HighlightRegionType.KEYWORD, 'set bbb(');
    assertNoRegion(HighlightRegionType.KEYWORD, 'set = 42');
  }

  Future<void> test_KEYWORD_show() async {
    addTestFile('''
import 'foo.dart' show Foo;
void f() {
  var show = 42;
}''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'show Foo');
    assertNoRegion(HighlightRegionType.KEYWORD, 'show = 42');
  }

  Future<void> test_KEYWORD_static() async {
    addTestFile('''
class A {
  static aaa;
  static bbb() {}
}
void f() {
  var static = 42;
}''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'static aaa;');
    assertHasRegion(HighlightRegionType.KEYWORD, 'static bbb()');
    assertNoRegion(HighlightRegionType.KEYWORD, 'static = 42');
  }

  Future<void> test_KEYWORD_sync() async {
    addTestFile('''
fa() sync {}
fb() sync* {}
void f() {
  bool sync = false;
}
''');
    await prepareHighlights();
    assertHasStringRegion(HighlightRegionType.KEYWORD, 'sync');
    assertHasStringRegion(HighlightRegionType.KEYWORD, 'sync*');
    assertNoRegion(HighlightRegionType.KEYWORD, 'sync = false');
  }

  Future<void> test_KEYWORD_typedef() async {
    addTestFile('''
typedef A();
typedef B = void Function();
typedef C = List<int>;
void f() {
  var typedef = 42;
}''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'typedef A();');
    assertHasRegion(HighlightRegionType.KEYWORD, 'typedef B =');
    assertHasRegion(HighlightRegionType.KEYWORD, 'typedef C =');
    assertNoRegion(HighlightRegionType.KEYWORD, 'typedef = 42');
  }

  Future<void> test_KEYWORD_void() async {
    addTestFile('''
void f() {
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'void f()');
  }

  Future<void> test_KEYWORD_yield() async {
    addTestFile('''
void f() async* {
  yield 42;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'yield 42');
  }

  Future<void> test_KEYWORD_yieldStar() async {
    addTestFile('''
void f() async* {
  yield* [];
}
''');
    await prepareHighlights();
    assertHasStringRegion(HighlightRegionType.KEYWORD, 'yield*');
  }

  Future<void> test_LABEL() async {
    addTestFile('''
void f() {
myLabel:
  while (true) {
    break myLabel;
  }
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.LABEL, 'myLabel:');
    assertHasRegion(HighlightRegionType.LABEL, 'myLabel;');
  }

  Future<void> test_LIBRARY_NAME_libraryDirective() async {
    addTestFile('''
library my.lib.name;
''');
    await prepareHighlights();
    assertHasStringRegion(HighlightRegionType.LIBRARY_NAME, 'my.lib.name');
  }

  Future<void> test_LIBRARY_NAME_partOfDirective() async {
    _addLibraryForTestPart();
    addTestFile('''
part of my.lib.name;
''');
    await prepareHighlights();
    assertHasStringRegion(HighlightRegionType.LIBRARY_NAME, 'my.lib.name');
  }

  Future<void> test_LITERAL_BOOLEAN() async {
    addTestFile('var V = true;');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.LITERAL_BOOLEAN, 'true;');
  }

  Future<void> test_LITERAL_DOUBLE() async {
    addTestFile('''
var V1 = 4.2;
var V2 = 1_2.3_4;
var V3 = 33e44;
var V4 = 3_3e4_4;
var V5 = 33.44e55;
var V6 = 3_3.4_4e5_5;
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.LITERAL_DOUBLE, '4.2;', '4.2'.length);
    assertHasRegion(
      HighlightRegionType.LITERAL_DOUBLE,
      '1_2.3_4;',
      '1_2.3_4'.length,
    );
    assertHasRegion(
      HighlightRegionType.LITERAL_DOUBLE,
      '33e44;',
      '33e44'.length,
    );
    assertHasRegion(
      HighlightRegionType.LITERAL_DOUBLE,
      '3_3e4_4;',
      '3_3e4_4'.length,
    );
    assertHasRegion(
      HighlightRegionType.LITERAL_DOUBLE,
      '33.44e55;',
      '33.44e55'.length,
    );
    assertHasRegion(
      HighlightRegionType.LITERAL_DOUBLE,
      '3_3.4_4e5_5;',
      '3_3.4_4e5_5'.length,
    );
  }

  Future<void> test_LITERAL_INTEGER() async {
    addTestFile('''
var V1 = 42;
var V2 = 1_000_000;
var V3 = 0x123;
var V4 = 0x12_34;
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.LITERAL_INTEGER, '42;');
    assertHasRegion(
      HighlightRegionType.LITERAL_INTEGER,
      '1_000_000;',
      '1_000_000'.length,
    );
    assertHasRegion(
      HighlightRegionType.LITERAL_INTEGER,
      '0x123;',
      '0x123'.length,
    );
    assertHasRegion(
      HighlightRegionType.LITERAL_INTEGER,
      '0x12_34;',
      '0x12_34'.length,
    );
  }

  Future<void> test_LITERAL_LIST() async {
    addTestFile('var V = <int>[1, 2, 3];');
    await prepareHighlights();
    assertHasStringRegion(HighlightRegionType.LITERAL_LIST, '<int>[1, 2, 3]');
  }

  Future<void> test_LITERAL_MAP() async {
    addTestFile("var V = const <int, String>{1: 'a', 2: 'b', 3: 'c'};");
    await prepareHighlights();
    assertHasStringRegion(
      HighlightRegionType.LITERAL_MAP,
      "const <int, String>{1: 'a', 2: 'b', 3: 'c'}",
    );
  }

  Future<void> test_LITERAL_STRING() async {
    addTestFile('var V = "abc";');
    await prepareHighlights();
    assertHasRegion(
      HighlightRegionType.LITERAL_STRING,
      '"abc";',
      '"abc"'.length,
    );
  }

  Future<void> test_LOCAL_FUNCTION() async {
    addTestFile('''
void f() {
  fff() {}
  fff();
  fff;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.LOCAL_FUNCTION_DECLARATION, 'fff() {}');
    assertHasRegion(HighlightRegionType.LOCAL_FUNCTION_REFERENCE, 'fff();');
    assertHasRegion(HighlightRegionType.LOCAL_FUNCTION_TEAR_OFF, 'fff;');
  }

  Future<void> test_LOCAL_VARIABLE() async {
    addTestFile('''
void f() {
  int vvv = 0;
  vvv;
  vvv = 1;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.LOCAL_VARIABLE_DECLARATION, 'vvv = 0');
    assertHasRegion(HighlightRegionType.LOCAL_VARIABLE_REFERENCE, 'vvv;');
    assertHasRegion(HighlightRegionType.LOCAL_VARIABLE_REFERENCE, 'vvv = 1;');
  }

  Future<void> test_METHOD() async {
    addTestFile('''
class A {
  aaa() {}
  static bbb() {}
}
void f(A a) {
  a.aaa();
  a.aaa;
  A.bbb();
  A.bbb;
}
''');
    await prepareHighlights();
    assertHasRegion(
      HighlightRegionType.INSTANCE_METHOD_DECLARATION,
      'aaa() {}',
    );
    assertHasRegion(HighlightRegionType.STATIC_METHOD_DECLARATION, 'bbb() {}');
    assertHasRegion(HighlightRegionType.INSTANCE_METHOD_REFERENCE, 'aaa();');
    assertHasRegion(HighlightRegionType.INSTANCE_METHOD_TEAR_OFF, 'aaa;');
    assertHasRegion(HighlightRegionType.STATIC_METHOD_REFERENCE, 'bbb();');
    assertHasRegion(HighlightRegionType.STATIC_METHOD_TEAR_OFF, 'bbb;');
  }

  Future<void> test_METHOD_bestType() async {
    addTestFile('''
void f(p) {
  if (p is List) {
    p.add(null);
  }
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.INSTANCE_METHOD_REFERENCE, 'add(null)');
  }

  Future<void> test_METHOD_dotShorthand() async {
    addTestFile('''
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; // compile-time error
  B b = .bMethod();
  B bb = .bMethod; // compile-time error
  Mixin m = .dMethod();
  Mixin mm = .dMethod; // compile-time error
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.STATIC_METHOD_REFERENCE, 'aMethod();');
    assertHasRegion(HighlightRegionType.STATIC_METHOD_TEAR_OFF, 'aMethod;');
    assertHasRegion(HighlightRegionType.STATIC_METHOD_REFERENCE, 'bMethod();');
    assertHasRegion(HighlightRegionType.STATIC_METHOD_TEAR_OFF, 'bMethod;');
    assertHasRegion(HighlightRegionType.STATIC_METHOD_REFERENCE, 'dMethod();');
    assertHasRegion(HighlightRegionType.STATIC_METHOD_TEAR_OFF, 'dMethod;');
  }

  Future<void> test_mixin() async {
    var testCode = TestCode.parseNormalized(r'''
mixin M on int {}
''');
    addTestFile(testCode.code);
    await prepareHighlights();
    assertHighlightText(testCode, -1, r'''
1:1 |mixin| KEYWORD
1:7 |M| MIXIN
1:9 |on| KEYWORD
1:12 |int| CLASS
''');
  }

  Future<void> test_mixin_base() async {
    var testCode = TestCode.parseNormalized(r'''
base mixin M {}
''');
    addTestFile(testCode.code);
    await prepareHighlights();
    assertHighlightText(testCode, -1, r'''
1:1 |base| KEYWORD
1:6 |mixin| KEYWORD
1:12 |M| MIXIN
''');
  }

  Future<void> test_namedExpression_namedParameter() async {
    addTestFile('''
void f({int a}) {}

void g() {
  f(a: 0);
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.PARAMETER_DECLARATION, 'a})');
    assertHasRegion(HighlightRegionType.PARAMETER_REFERENCE, 'a: 0');
  }

  Future<void> test_namedType_extensionType() async {
    var testCode = TestCode.parseNormalized(r'''
extension type A<T>(T it) {}
void f([!A<int>!] a) {}
''');
    addTestFile(testCode.code);
    await prepareHighlights();
    assertHighlightText(testCode, 0, r'''
2:8 |A| EXTENSION_TYPE
2:10 |int| CLASS
''');
  }

  Future<void> test_PARAMETER() async {
    addTestFile('''
void f(int p) {
  p;
  p = 42;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.PARAMETER_DECLARATION, 'p) {');
    assertHasRegion(HighlightRegionType.PARAMETER_REFERENCE, 'p;');
    assertHasRegion(HighlightRegionType.PARAMETER_REFERENCE, 'p = 42');
  }

  Future<void> test_PARAMETER_named() async {
    addTestFile('''
class C {
  final int aaa;
  C({this.aaa, int bbb});
}
void f() {
  new C(aaa: 1, bbb: 2);
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.INSTANCE_FIELD_REFERENCE, 'aaa,');
    assertHasRegion(HighlightRegionType.PARAMETER_DECLARATION, 'bbb}');
    assertHasRegion(HighlightRegionType.PARAMETER_REFERENCE, 'aaa: 1');
    assertHasRegion(HighlightRegionType.PARAMETER_REFERENCE, 'bbb: 2');
  }

  Future<void> test_PARAMETER_named_anywhere() async {
    addTestFile('''
void f(int aaa, int bbb, {int? ccc, int? ddd}) {}

void g() {
  f(0, ccc: 2, 1, ddd: 3);
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.PARAMETER_REFERENCE, 'ccc: 2');
    assertHasRegion(HighlightRegionType.PARAMETER_REFERENCE, 'ddd: 3');
  }

  Future<void> test_PARAMETER_super_children() async {
    addTestFile('''
const V1 = 1;
class A {
  A(Object aaa);
}
class B extends A {
  B(@V1 int super.aaa<T>(double a));
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.TOP_LEVEL_GETTER_REFERENCE, 'V1 int');
    assertHasRegion(HighlightRegionType.CLASS, 'int');
    assertHasRegion(HighlightRegionType.CLASS, 'double');
    assertHasRegion(HighlightRegionType.TYPE_PARAMETER, 'T>');
    assertHasRegion(HighlightRegionType.PARAMETER_DECLARATION, 'a));');
  }

  Future<void> test_PARAMETER_super_requiredNamed() async {
    addTestFile('''
class A {
  A({required int aaa});
}
class B extends A {
  B({required super.aaa });
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'required super.aaa');
    assertHasRegion(HighlightRegionType.KEYWORD, 'super.aaa');
    assertHasRegion(HighlightRegionType.PARAMETER_DECLARATION, 'aaa ');
  }

  Future<void> test_PARAMETER_super_requiredPositional() async {
    addTestFile('''
class A {
  A(int aaa);
}
class B extends A {
  B(super.aaa );
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'super.aaa');
    assertHasRegion(HighlightRegionType.PARAMETER_DECLARATION, 'aaa ');
  }

  Future<void> test_patternVariableDeclaration_final() async {
    addTestFile('''
void f(Object o) {
  final i as int = o;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'final');
  }

  Future<void> test_patternVariableDeclaration_var() async {
    addTestFile('''
void f(Object o) {
  var i as int = o;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'var');
  }

  Future<void> test_propertyAccess_extensionTypeName() async {
    var testCode = TestCode.parseNormalized(r'''
extension type A.named(T it) {
  static const V = 0;
}
void f() {
  [!A.V!];
}
''');
    addTestFile(testCode.code);
    await prepareHighlights();
    assertHighlightText(testCode, 0, r'''
5:3 |A| EXTENSION_TYPE
5:5 |V| STATIC_GETTER_REFERENCE
''');
  }

  Future<void> test_record_field() async {
    addTestFile(r'''
void f((int, {int field1}) record) {
  record.$1; // 1
  record.field1; // 2
  (1,).$1; // 3
  (field1: 1).field1; // 4
  (1,).notResolved;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.INSTANCE_GETTER_REFERENCE, r'$1; // 1');
    assertHasRegion(
      HighlightRegionType.INSTANCE_GETTER_REFERENCE,
      'field1; // 2',
    );
    assertHasRegion(HighlightRegionType.INSTANCE_GETTER_REFERENCE, r'$1; // 3');
    assertHasRegion(
      HighlightRegionType.UNRESOLVED_INSTANCE_MEMBER_REFERENCE,
      'notResolved',
    );
  }

  Future<void> test_recordTypeAnnotation_named() async {
    addTestFile('''
({int f1, String f2})? r;
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.CLASS, 'int f1');
    assertHasRegion(HighlightRegionType.FIELD, 'f1');
    assertHasRegion(HighlightRegionType.CLASS, 'String f2');
    assertHasRegion(HighlightRegionType.FIELD, 'f2');
  }

  Future<void> test_recordTypeAnnotation_positional() async {
    addTestFile('''
(int, String f2)? r;
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.CLASS, 'int, ');
    assertHasRegion(HighlightRegionType.CLASS, 'String f2)');
    assertHasRegion(HighlightRegionType.FIELD, 'f2)');
  }

  Future<void> test_recordTypeLiteral_named() async {
    var code = '(0, f1: 1, f2: 2)';
    addTestFile('''
final r = $code;
''');
    await prepareHighlights();
    assertHasStringRegion(HighlightRegionType.LITERAL_RECORD, code);
    assertHasRegion(HighlightRegionType.LITERAL_INTEGER, '0,');
    assertHasRegion(HighlightRegionType.PARAMETER_REFERENCE, 'f1:');
    assertHasRegion(HighlightRegionType.LITERAL_INTEGER, '1,');
    assertHasRegion(HighlightRegionType.PARAMETER_REFERENCE, 'f2:');
    assertHasRegion(HighlightRegionType.LITERAL_INTEGER, '2)');
  }

  Future<void> test_SETTER_DECLARATION() async {
    addTestFile('''
set aaa(x) {}
class A {
  set bbb(x) {}
  static set ccc(x) {}
}
void f(A a) {
  aaa = 1;
  a.bbb = 2;
  A.ccc = 3;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.TOP_LEVEL_SETTER_DECLARATION, 'aaa(x)');
    assertHasRegion(HighlightRegionType.INSTANCE_SETTER_DECLARATION, 'bbb(x)');
    assertHasRegion(HighlightRegionType.STATIC_SETTER_DECLARATION, 'ccc(x)');
    assertHasRegion(HighlightRegionType.TOP_LEVEL_SETTER_REFERENCE, 'aaa = 1');
    assertHasRegion(HighlightRegionType.INSTANCE_SETTER_REFERENCE, 'bbb = 2');
    assertHasRegion(HighlightRegionType.STATIC_SETTER_REFERENCE, 'ccc = 3');
  }

  Future<void> test_STATIC_FIELD() async {
    addTestFile('''
class A {
  static aaa = 1;
  static get bbb => null;
  static set ccc(x) {}
}
void f() {
  A.aaa = 2;
  A.bbb;
  A.ccc = 3;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.STATIC_FIELD_DECLARATION, 'aaa = 1');
    assertHasRegion(HighlightRegionType.STATIC_SETTER_REFERENCE, 'aaa = 2');
    assertHasRegion(HighlightRegionType.STATIC_GETTER_REFERENCE, 'bbb;');
    assertHasRegion(HighlightRegionType.STATIC_SETTER_REFERENCE, 'ccc = 3');
  }

  Future<void> test_switchExpression_switch() async {
    addTestFile('''
String f(Object o) {
  return switch (o) {
    3 => '3';
  };
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'switch');
  }

  Future<void> test_TOP_LEVEL_FUNCTION() async {
    addTestFile('''
fff(p) {}
void f() {
  fff(42);
  fff;
}
''');
    await prepareHighlights();
    assertHasRegion(
      HighlightRegionType.TOP_LEVEL_FUNCTION_DECLARATION,
      'fff(p) {}',
    );
    assertHasRegion(
      HighlightRegionType.TOP_LEVEL_FUNCTION_REFERENCE,
      'fff(42)',
    );
    assertHasRegion(HighlightRegionType.TOP_LEVEL_FUNCTION_TEAR_OFF, 'fff;');
  }

  Future<void> test_TOP_LEVEL_VARIABLE() async {
    addTestFile('''
const V1 = 1;
var V2 = 2;
@V1 // annotation
void f() {
  print(V1);
  V2 = 3;
}
''');
    await prepareHighlights();
    assertHasRegion(
      HighlightRegionType.TOP_LEVEL_VARIABLE_DECLARATION,
      'V1 = 1',
    );
    assertHasRegion(
      HighlightRegionType.TOP_LEVEL_VARIABLE_DECLARATION,
      'V2 = 2',
    );
    assertHasRegion(
      HighlightRegionType.TOP_LEVEL_GETTER_REFERENCE,
      'V1 // annotation',
    );
    assertHasRegion(HighlightRegionType.TOP_LEVEL_GETTER_REFERENCE, 'V1);');
    assertHasRegion(HighlightRegionType.TOP_LEVEL_SETTER_REFERENCE, 'V2 = 3');
  }

  Future<void> test_TYPE_NAME_DYNAMIC() async {
    addTestFile('''
dynamic f() {
  dynamic = 42;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.TYPE_NAME_DYNAMIC, 'dynamic f()');
    assertNoRegion(HighlightRegionType.IDENTIFIER_DEFAULT, 'dynamic f()');
    assertNoRegion(HighlightRegionType.TYPE_NAME_DYNAMIC, 'dynamic = 42');
  }

  Future<void> test_TYPE_PARAMETER() async {
    addTestFile('''
class A<T> {
  T fff;
  T mmm(T p) => null;
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.TYPE_PARAMETER, 'T> {');
    assertHasRegion(HighlightRegionType.TYPE_PARAMETER, 'T fff;');
    assertHasRegion(HighlightRegionType.TYPE_PARAMETER, 'T mmm(');
    assertHasRegion(HighlightRegionType.TYPE_PARAMETER, 'T p)');
  }

  Future<void> test_typedef_classic() async {
    var testCode = TestCode.parseNormalized(r'''
typedef int MyFunction<T extends num>(T a, String b);
''');
    addTestFile(testCode.code);
    await prepareHighlights();
    assertHighlightText(testCode, -1, r'''
1:1 |typedef| KEYWORD
1:9 |int| CLASS
1:13 |MyFunction| FUNCTION_TYPE_ALIAS
1:24 |T| TYPE_PARAMETER
1:26 |extends| KEYWORD
1:34 |num| CLASS
1:39 |T| TYPE_PARAMETER
1:41 |a| PARAMETER_DECLARATION
1:44 |String| CLASS
1:51 |b| PARAMETER_DECLARATION
''');
  }

  Future<void> test_typedef_classic_functionTypedFormalParameter() async {
    var testCode = TestCode.parseNormalized(r'''
typedef int MyFunction(bool foo());
''');
    addTestFile(testCode.code);
    await prepareHighlights();
    assertHighlightText(testCode, -1, r'''
1:1 |typedef| KEYWORD
1:9 |int| CLASS
1:13 |MyFunction| FUNCTION_TYPE_ALIAS
1:24 |bool| CLASS
1:29 |foo| PARAMETER_DECLARATION
''');
  }

  Future<void> test_typedef_classic_reference() async {
    var testCode = TestCode.parseNormalized(r'''
typedef void MyFunction();
void f(MyFunction a) {}
''');
    addTestFile(testCode.code);
    await prepareHighlights();
    assertHighlightText(testCode, -1, r'''
1:1 |typedef| KEYWORD
1:9 |void| KEYWORD
1:14 |MyFunction| FUNCTION_TYPE_ALIAS
2:1 |void| KEYWORD
2:6 |f| TOP_LEVEL_FUNCTION_DECLARATION
2:8 |MyFunction| FUNCTION_TYPE_ALIAS
2:19 |a| PARAMETER_DECLARATION
''');
  }

  Future<void> test_typedef_modern_dynamicType() async {
    var testCode = TestCode.parseNormalized(r'''
typedef A = dynamic;
''');
    addTestFile(testCode.code);
    await prepareHighlights();
    assertHighlightText(testCode, -1, r'''
1:1 |typedef| KEYWORD
1:9 |A| TYPE_ALIAS
1:13 |dynamic| TYPE_NAME_DYNAMIC
''');
  }

  Future<void> test_typedef_modern_functionType() async {
    var testCode = TestCode.parseNormalized(r'''
typedef MyFunction = int Function(int a, {required String b});
''');
    addTestFile(testCode.code);
    await prepareHighlights();
    assertHighlightText(testCode, -1, r'''
1:1 |typedef| KEYWORD
1:9 |MyFunction| FUNCTION_TYPE_ALIAS
1:22 |int| CLASS
1:26 |Function| KEYWORD
1:35 |int| CLASS
1:39 |a| PARAMETER_DECLARATION
1:43 |required| KEYWORD
1:52 |String| CLASS
1:59 |b| PARAMETER_DECLARATION
''');
  }

  Future<void> test_typedef_modern_interfaceType() async {
    var testCode = TestCode.parseNormalized(r'''
typedef A = double;
''');
    addTestFile(testCode.code);
    await prepareHighlights();
    assertHighlightText(testCode, -1, r'''
1:1 |typedef| KEYWORD
1:9 |A| TYPE_ALIAS
1:13 |double| CLASS
''');
  }

  Future<void> test_typedef_modern_interfaceType_typeArguments() async {
    var testCode = TestCode.parseNormalized(r'''
typedef A = Map<int, String>;
''');
    addTestFile(testCode.code);
    await prepareHighlights();
    assertHighlightText(testCode, -1, r'''
1:1 |typedef| KEYWORD
1:9 |A| TYPE_ALIAS
1:13 |Map| CLASS
1:17 |int| CLASS
1:22 |String| CLASS
''');
  }

  Future<void> test_typedef_modern_reference() async {
    var testCode = TestCode.parseNormalized(r'''
typedef A = void Function();
void f(A a) {}
''');
    addTestFile(testCode.code);
    await prepareHighlights();
    assertHighlightText(testCode, -1, r'''
1:1 |typedef| KEYWORD
1:9 |A| FUNCTION_TYPE_ALIAS
1:13 |void| KEYWORD
1:18 |Function| KEYWORD
2:1 |void| KEYWORD
2:6 |f| TOP_LEVEL_FUNCTION_DECLARATION
2:8 |A| FUNCTION_TYPE_ALIAS
2:10 |a| PARAMETER_DECLARATION
''');
  }

  Future<void>
  test_UNRESOLVED_INSTANCE_MEMBER_REFERENCE_dynamicVarTarget() async {
    addTestFile('''
void f(p) {
  p.aaa;
  p.aaa++;
  p.aaa += 0;
  ++p.aaa; // ++
  p.aaa = 0;
  p.bbb(0);
  ''.length.ccc().ddd();
}
''');
    await prepareHighlights();
    var type = HighlightRegionType.UNRESOLVED_INSTANCE_MEMBER_REFERENCE;
    assertHasRegion(type, 'aaa');
    assertHasRegion(type, 'aaa++');
    assertHasRegion(type, 'aaa += 0');
    assertHasRegion(type, 'aaa; // ++');
    assertHasRegion(type, 'aaa =');
    assertHasRegion(type, 'bbb(');
    assertHasRegion(type, 'ddd()');
  }

  Future<void>
  test_UNRESOLVED_INSTANCE_MEMBER_REFERENCE_nonDynamicTarget() async {
    addTestFile('''
import 'dart:math' as math;
void f(String str) {
  new Object().aaa();
  math.bbb();
  str.ccc();
}
class A {
  m() {
    unresolved(1);
    this.unresolved(2);
    super.unresolved(3);
  }
}
''');
    await prepareHighlights();
    var type = HighlightRegionType.IDENTIFIER_DEFAULT;
    assertHasRegion(type, 'aaa()');
    assertHasRegion(type, 'bbb()');
    assertHasRegion(type, 'ccc()');
    assertHasRegion(type, 'unresolved(1)');
    assertHasRegion(type, 'unresolved(2)');
    assertHasRegion(type, 'unresolved(3)');
  }

  Future<void> test_whenClause_when() async {
    addTestFile('''
void f(Object o) {
  switch (o) {
    case [int a, int n] when n > 0:
      return;
  }
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'when');
  }

  Future<void> test_wildcardPattern_final() async {
    addTestFile('''
void f(Object o) {
  switch (o) {
    case (final _ as int):
      return;
  }
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'final');
  }

  Future<void> test_wildcardPattern_var() async {
    addTestFile('''
void f(Object o) {
  switch (o) {
    case (var _ as int):
      return;
  }
}
''');
    await prepareHighlights();
    assertHasRegion(HighlightRegionType.KEYWORD, 'var');
  }

  /// Returns the textual dump of the highlight regions that intersect with
  /// the [index]th range in [testCode] (or all code if `-1`).
  String _getHighlightText(TestCode testCode, int index) {
    var window = index == -1
        ? SourceRange(0, testCode.code.length)
        : testCode.ranges[index].sourceRange;

    // TODO(scheglov): Apparently, we don't sort in the server.
    var sortedRegions = regions.sortedBy<num>((e) => e.offset);

    var buffer = StringBuffer();
    for (var region in sortedRegions) {
      var offset = region.offset;
      var end = offset + region.length;
      if (window.contains(offset) || window.contains(end)) {
        var location = LineInfo.fromContent(testCode.code).getLocation(offset);
        var regionDesc = '${location.lineNumber}:${location.columnNumber}';
        var regionCode = testCode.code.substring(offset, end);
        buffer.writeln('$regionDesc |$regionCode| ${region.type.name}');
      }
    }
    return buffer.toString();
  }
}

class HighlightsTestSupport extends PubPackageAnalysisServerTest {
  late List<HighlightRegion> regions;

  final Completer<void> _resultsAvailable = Completer();

  void assertHasRange(HighlightRegionType type, TestCodeRange range) {
    assertHasRawRegion(
      type,
      range.sourceRange.offset,
      range.sourceRange.length,
    );
  }

  void assertHasRawRegion(HighlightRegionType type, int offset, int length) {
    for (var region in regions) {
      if (region.offset == offset &&
          region.length == length &&
          region.type == type) {
        return;
      }
    }
    // Attempt to figure out what's wrong with the expectation.
    for (var region in regions) {
      if (region.offset == offset && region.length == length) {
        fail(
          'Expected a type of $type, but found a type of ${region.type} at '
          'offset=$offset; length=$length.',
        );
      }
    }
    // Fall back to a general failure message.
    regions.sort((first, second) => first.offset.compareTo(second.offset));
    fail(
      'Expected to find (offset=$offset; length=$length; type=$type) in\n'
      '${regions.join('\n')}',
    );
  }

  void assertHasRegion(
    HighlightRegionType type,
    String search, [
    int length = -1,
  ]) {
    var offset = findOffset(search);
    length = findRegionLength(search, length);
    assertHasRawRegion(type, offset, length);
  }

  void assertHasStringRegion(HighlightRegionType type, String str) {
    str = normalizeNewlinesForPlatform(str);
    var offset = findOffset(str);
    var length = str.length;
    assertHasRawRegion(type, offset, length);
  }

  void assertNoRawRegion(HighlightRegionType type, int offset, int length) {
    for (var region in regions) {
      if (region.offset == offset &&
          region.length == length &&
          region.type == type) {
        fail(
          'Not expected to find (offset=$offset; length=$length; type=$type) in\n'
          '${regions.join('\n')}',
        );
      }
    }
  }

  void assertNoRegion(
    HighlightRegionType type,
    String search, [
    int length = -1,
  ]) {
    var offset = findOffset(search);
    length = findRegionLength(search, length);
    assertNoRawRegion(type, offset, length);
  }

  int findRegionLength(String search, int length) {
    if (length == -1) {
      length = 0;
      while (length < search.length) {
        var c = search.codeUnitAt(length);
        if (length == 0 && c == '@'.codeUnitAt(0)) {
          length++;
          continue;
        }
        if (!(c >= 'a'.codeUnitAt(0) && c <= 'z'.codeUnitAt(0) ||
            c >= 'A'.codeUnitAt(0) && c <= 'Z'.codeUnitAt(0) ||
            c >= '0'.codeUnitAt(0) && c <= '9'.codeUnitAt(0) ||
            c == r'$'.codeUnitAt(0))) {
          break;
        }
        length++;
      }
    }
    return length;
  }

  Future<void> prepareHighlights() async {
    await addAnalysisSubscription(AnalysisService.HIGHLIGHTS, testFile);
    return _resultsAvailable.future;
  }

  @override
  void processNotification(Notification notification) {
    if (notification.event == serverNotificationError) {
      print('SERVER_NOTIFICATION_ERROR: ${notification.toJson()}');
      _resultsAvailable.complete();
      fail('SERVER_NOTIFICATION_ERROR');
    }
    if (notification.event == analysisNotificationHighlights) {
      var params = AnalysisHighlightsParams.fromNotification(
        notification,
        clientUriConverter: server.uriConverter,
      );
      if (params.file == testFile.path) {
        regions = params.regions;
        _resultsAvailable.complete();
      }
    }
  }

  @override
  Future<void> setUp() async {
    super.setUp();
    await setRoots(included: [workspaceRootPath], excluded: []);
  }

  void _addLibraryForTestPart() {
    newFile('$testPackageLibPath/my_lib.dart', '''
library my.lib.name;
part 'test.dart';
    ''');
  }
}

@reflectiveTest
class HighlightTypeTest {
  void test_constructor() {
    expect(
      HighlightRegionType.CLASS,
      HighlightRegionType.values.byName(HighlightRegionType.CLASS.name),
    );
  }

  void test_toString() {
    expect(HighlightRegionType.CLASS.toString(), 'HighlightRegionType.CLASS');
  }

  void test_valueOf_unknown() {
    expect(() {
      HighlightRegionType.values.byName('no-such-type');
    }, throwsArgumentError);
  }
}
