| // 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 'package:analysis_server/protocol/protocol_generated.dart'; | |
| import 'package:analyzer_plugin/protocol/protocol_common.dart'; | |
| import 'package:test/test.dart'; | |
| import 'package:test_reflective_loader/test_reflective_loader.dart'; | |
| import '../support/integration_tests.dart'; | |
| void main() { | |
| defineReflectiveSuite(() { | |
| defineReflectiveTests(AnalysisHighlightsTest); | |
| }); | |
| } | |
| @reflectiveTest | |
| class AnalysisHighlightsTest extends AbstractAnalysisServerIntegrationTest { | |
| late Map<HighlightRegionType, Set<String>> highlights; | |
| void check(HighlightRegionType type, List<String> expected) { | |
| expect(highlights[type], equals(expected.toSet())); | |
| highlights.remove(type); | |
| } | |
| Future<void> computeHighlights(String pathname, String text) async { | |
| writeFile(pathname, text); | |
| await standardAnalysisSetup(); | |
| await analysisFinished; | |
| // Set up the handler before we set the subscription because it appears that | |
| // the implementation of setSubscriptions may trigger notifications before | |
| // it returns if there are cached results. | |
| onAnalysisHighlights.listen((AnalysisHighlightsParams params) { | |
| // Map from highlight type to highlighted text. | |
| expect(params.file, equals(pathname)); | |
| highlights = <HighlightRegionType, Set<String>>{}; | |
| for (var region in params.regions) { | |
| var startIndex = region.offset; | |
| var endIndex = startIndex + region.length; | |
| var highlightedText = text.substring(startIndex, endIndex); | |
| var type = region.type; | |
| highlights.putIfAbsent(type, () => {}).add(highlightedText); | |
| } | |
| }); | |
| await sendAnalysisSetSubscriptions({ | |
| AnalysisService.HIGHLIGHTS: [pathname], | |
| }); | |
| await analysisFinished; | |
| } | |
| @override | |
| Future<void> startServer({int? diagnosticPort, int? servicePort}) { | |
| return server.start( | |
| dartSdkPath: dartSdkPath, | |
| diagnosticPort: diagnosticPort, | |
| servicePort: servicePort, | |
| useAnalysisHighlight2: true, | |
| ); | |
| } | |
| Future<void> test_highlights() async { | |
| var pathname = sourcePath('test.dart'); | |
| var text = r''' | |
| import 'dart:async' as async; | |
| /** | |
| * Doc comment | |
| */ | |
| class Class<TypeParameter> { | |
| Class() { | |
| field = {1.0: [].toList()}; | |
| } | |
| Class.constructor() { | |
| dynamic local = true; | |
| field = {2: local}; | |
| } | |
| Map field = {3: 4}; | |
| static int staticField = 0; | |
| method() { | |
| // End of line comment | |
| /* Block comment */ | |
| } | |
| static staticMethod() { | |
| } | |
| get getter { | |
| } | |
| set setter(int parameter) { | |
| print(parameter); | |
| } | |
| } | |
| class Class2<TypeParameter> extends Class<TypeParameter> { | |
| @override | |
| method() { | |
| } | |
| } | |
| typedef functionType(); | |
| typedef genericTypeDef = double; | |
| function(dynamicType) { | |
| print('string'); | |
| unresolvedIdentifier = 42; | |
| return async.Future.wait([]); | |
| } | |
| int topLevelVariable = 0; | |
| '''; | |
| await computeHighlights(pathname, text); | |
| // There should be 1 error due to the fact that unresolvedIdentifier is | |
| // unresolved. | |
| expect(currentAnalysisErrors[pathname], hasLength(1)); | |
| check(HighlightRegionType.ANNOTATION, ['@override']); | |
| check(HighlightRegionType.CLASS, [ | |
| 'Class', | |
| 'Class2', | |
| 'double', | |
| 'Future', | |
| 'Map', | |
| 'int', | |
| ]); | |
| check(HighlightRegionType.COMMENT_BLOCK, ['/* Block comment */']); | |
| check(HighlightRegionType.COMMENT_DOCUMENTATION, [ | |
| '/**\n * Doc comment\n */', | |
| ]); | |
| check(HighlightRegionType.COMMENT_END_OF_LINE, ['// End of line comment']); | |
| check(HighlightRegionType.CONSTRUCTOR, ['constructor']); | |
| check(HighlightRegionType.DIRECTIVE, ["import 'dart:async' as async;"]); | |
| check(HighlightRegionType.DYNAMIC_PARAMETER_DECLARATION, ['dynamicType']); | |
| check(HighlightRegionType.INSTANCE_FIELD_DECLARATION, ['field']); | |
| check(HighlightRegionType.INSTANCE_SETTER_REFERENCE, ['field']); | |
| check(HighlightRegionType.STATIC_FIELD_DECLARATION, ['staticField']); | |
| check(HighlightRegionType.TOP_LEVEL_FUNCTION_REFERENCE, ['print']); | |
| check(HighlightRegionType.TOP_LEVEL_FUNCTION_DECLARATION, ['function']); | |
| check(HighlightRegionType.FUNCTION_TYPE_ALIAS, ['functionType']); | |
| check(HighlightRegionType.TYPE_ALIAS, ['genericTypeDef']); | |
| check(HighlightRegionType.INSTANCE_GETTER_DECLARATION, ['getter']); | |
| check(HighlightRegionType.IDENTIFIER_DEFAULT, ['unresolvedIdentifier']); | |
| check(HighlightRegionType.IMPORT_PREFIX, ['async']); | |
| check(HighlightRegionType.KEYWORD, [ | |
| 'as', | |
| 'class', | |
| 'extends', | |
| 'get', | |
| 'import', | |
| 'return', | |
| 'set', | |
| 'static', | |
| 'true', | |
| 'typedef', | |
| ]); | |
| check(HighlightRegionType.LITERAL_BOOLEAN, ['true']); | |
| check(HighlightRegionType.LITERAL_DOUBLE, ['1.0']); | |
| check(HighlightRegionType.LITERAL_INTEGER, ['2', '3', '4', '0', '42']); | |
| check(HighlightRegionType.LITERAL_LIST, ['[]']); | |
| check(HighlightRegionType.LITERAL_MAP, [ | |
| '{1.0: [].toList()}', | |
| '{2: local}', | |
| '{3: 4}', | |
| ]); | |
| check(HighlightRegionType.LITERAL_STRING, ["'dart:async'", "'string'"]); | |
| check(HighlightRegionType.DYNAMIC_LOCAL_VARIABLE_DECLARATION, ['local']); | |
| check(HighlightRegionType.DYNAMIC_LOCAL_VARIABLE_REFERENCE, ['local']); | |
| check(HighlightRegionType.INSTANCE_METHOD_REFERENCE, ['toList']); | |
| check(HighlightRegionType.INSTANCE_METHOD_DECLARATION, ['method']); | |
| check(HighlightRegionType.STATIC_METHOD_DECLARATION, ['staticMethod']); | |
| check(HighlightRegionType.STATIC_METHOD_REFERENCE, ['wait']); | |
| check(HighlightRegionType.PARAMETER_DECLARATION, ['parameter']); | |
| check(HighlightRegionType.PARAMETER_REFERENCE, ['parameter']); | |
| check(HighlightRegionType.INSTANCE_SETTER_DECLARATION, ['setter']); | |
| check(HighlightRegionType.TOP_LEVEL_GETTER_REFERENCE, ['override']); | |
| check(HighlightRegionType.TOP_LEVEL_VARIABLE_DECLARATION, [ | |
| 'topLevelVariable', | |
| ]); | |
| check(HighlightRegionType.TYPE_NAME_DYNAMIC, ['dynamic']); | |
| check(HighlightRegionType.TYPE_PARAMETER, ['TypeParameter']); | |
| expect(highlights, isEmpty); | |
| } | |
| Future<void> test_highlights_mixin() async { | |
| var pathname = sourcePath('test.dart'); | |
| var text = r''' | |
| mixin M on A implements B {} | |
| class A {} | |
| class B {} | |
| '''; | |
| await computeHighlights(pathname, text); | |
| expect(currentAnalysisErrors[pathname], hasLength(0)); | |
| check(HighlightRegionType.KEYWORD, ['class', 'implements', 'mixin', 'on']); | |
| } | |
| Future<void> test_highlights_tryCatch() async { | |
| var pathname = sourcePath('test.dart'); | |
| var text = r''' | |
| void f() { | |
| try { | |
| } on ArgumentError catch (error1, stackTrace1) { | |
| } on UnimplementedError catch (error2, stackTrace2) { | |
| } on IndexError catch (error2) { | |
| } catch (error3, stackTrace3) { | |
| } | |
| } | |
| '''; | |
| await computeHighlights(pathname, text); | |
| check(HighlightRegionType.KEYWORD, ['on', 'void', 'try', 'catch']); | |
| check(HighlightRegionType.CLASS, [ | |
| 'ArgumentError', | |
| 'UnimplementedError', | |
| 'IndexError', | |
| ]); | |
| check(HighlightRegionType.LOCAL_VARIABLE_DECLARATION, [ | |
| 'error1', | |
| 'stackTrace1', | |
| 'error2', | |
| 'stackTrace2', | |
| 'error3', | |
| 'stackTrace3', | |
| ]); | |
| } | |
| } |