d219bde703
Change-Id: I1812f7721915178e42956f988b9276bf8db0bfaa Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/164281 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
1289 lines
43 KiB
Dart
1289 lines
43 KiB
Dart
// 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:analysis_server/src/plugin/plugin_manager.dart';
|
|
import 'package:analysis_server/src/provisional/completion/completion_core.dart';
|
|
import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
|
|
import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
|
|
import 'package:analysis_server/src/services/completion/dart/contribution_sorter.dart';
|
|
import 'package:analyzer_plugin/protocol/protocol.dart' as plugin;
|
|
import 'package:analyzer_plugin/protocol/protocol_common.dart';
|
|
import 'package:analyzer_plugin/protocol/protocol_constants.dart' as plugin;
|
|
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
|
|
import 'package:test/test.dart';
|
|
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
|
|
|
import 'domain_completion_util.dart';
|
|
import 'mocks.dart';
|
|
|
|
void main() {
|
|
defineReflectiveSuite(() {
|
|
defineReflectiveTests(CompletionDomainHandlerGetSuggestionsTest);
|
|
defineReflectiveTests(CompletionDomainHandlerListTokenDetailsTest);
|
|
});
|
|
}
|
|
|
|
@reflectiveTest
|
|
class CompletionDomainHandlerGetSuggestionsTest
|
|
extends AbstractCompletionDomainTest {
|
|
Future<void> test_ArgumentList_constructor_named_fieldFormalParam() async {
|
|
// https://github.com/dart-lang/sdk/issues/31023
|
|
addTestFile('''
|
|
main() { new A(field: ^);}
|
|
class A {
|
|
A({this.field: -1}) {}
|
|
}
|
|
''');
|
|
await getSuggestions();
|
|
}
|
|
|
|
Future<void> test_ArgumentList_constructor_named_param_label() async {
|
|
addTestFile('main() { new A(^);}'
|
|
'class A { A({one, two}) {} }');
|
|
await getSuggestions();
|
|
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'one: ',
|
|
relevance: DART_RELEVANCE_NAMED_PARAMETER);
|
|
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'two: ',
|
|
relevance: DART_RELEVANCE_NAMED_PARAMETER);
|
|
expect(suggestions, hasLength(2));
|
|
}
|
|
|
|
Future<void> test_ArgumentList_factory_named_param_label() async {
|
|
addTestFile('main() { new A(^);}'
|
|
'class A { factory A({one, two}) => throw 0; }');
|
|
await getSuggestions();
|
|
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'one: ',
|
|
relevance: DART_RELEVANCE_NAMED_PARAMETER);
|
|
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'two: ',
|
|
relevance: DART_RELEVANCE_NAMED_PARAMETER);
|
|
expect(suggestions, hasLength(2));
|
|
}
|
|
|
|
Future<void> test_ArgumentList_imported_function_named_param() async {
|
|
addTestFile('main() { int.parse("16", ^);}');
|
|
await getSuggestions();
|
|
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'radix: ',
|
|
relevance: DART_RELEVANCE_NAMED_PARAMETER);
|
|
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'onError: ',
|
|
relevance: DART_RELEVANCE_NAMED_PARAMETER);
|
|
expect(suggestions, hasLength(2));
|
|
}
|
|
|
|
Future<void> test_ArgumentList_imported_function_named_param1() async {
|
|
addTestFile('main() { foo(o^);} foo({one, two}) {}');
|
|
await getSuggestions();
|
|
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'one: ',
|
|
relevance: DART_RELEVANCE_NAMED_PARAMETER);
|
|
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'two: ',
|
|
relevance: DART_RELEVANCE_NAMED_PARAMETER);
|
|
expect(suggestions, hasLength(2));
|
|
}
|
|
|
|
Future<void> test_ArgumentList_imported_function_named_param2() async {
|
|
addTestFile('mainx() {A a = new A(); a.foo(one: 7, ^);}'
|
|
'class A { foo({one, two}) {} }');
|
|
await getSuggestions();
|
|
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'two: ',
|
|
relevance: DART_RELEVANCE_NAMED_PARAMETER);
|
|
expect(suggestions, hasLength(1));
|
|
}
|
|
|
|
Future<void> test_ArgumentList_imported_function_named_param_label1() async {
|
|
addTestFile('main() { int.parse("16", r^: 16);}');
|
|
await getSuggestions();
|
|
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'radix',
|
|
relevance: DART_RELEVANCE_NAMED_PARAMETER);
|
|
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'onError',
|
|
relevance: DART_RELEVANCE_NAMED_PARAMETER);
|
|
expect(suggestions, hasLength(2));
|
|
}
|
|
|
|
Future<void> test_ArgumentList_imported_function_named_param_label3() async {
|
|
addTestFile('main() { int.parse("16", ^: 16);}');
|
|
await getSuggestions();
|
|
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'radix: ',
|
|
relevance: DART_RELEVANCE_NAMED_PARAMETER);
|
|
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'onError: ',
|
|
relevance: DART_RELEVANCE_NAMED_PARAMETER);
|
|
expect(suggestions, hasLength(2));
|
|
}
|
|
|
|
Future<void> test_catch() async {
|
|
addTestFile('main() {try {} ^}');
|
|
await getSuggestions();
|
|
assertHasResult(CompletionSuggestionKind.KEYWORD, 'on',
|
|
relevance: DART_RELEVANCE_KEYWORD);
|
|
assertHasResult(CompletionSuggestionKind.KEYWORD, 'catch',
|
|
relevance: DART_RELEVANCE_KEYWORD);
|
|
assertHasResult(CompletionSuggestionKind.KEYWORD, 'finally',
|
|
relevance: DART_RELEVANCE_KEYWORD);
|
|
expect(suggestions, hasLength(3));
|
|
}
|
|
|
|
Future<void> test_catch2() async {
|
|
addTestFile('main() {try {} on Foo {} ^}');
|
|
await getSuggestions();
|
|
assertHasResult(CompletionSuggestionKind.KEYWORD, 'on',
|
|
relevance: DART_RELEVANCE_KEYWORD);
|
|
assertHasResult(CompletionSuggestionKind.KEYWORD, 'catch',
|
|
relevance: DART_RELEVANCE_KEYWORD);
|
|
assertHasResult(CompletionSuggestionKind.KEYWORD, 'finally',
|
|
relevance: DART_RELEVANCE_KEYWORD);
|
|
assertHasResult(CompletionSuggestionKind.KEYWORD, 'for',
|
|
relevance: DART_RELEVANCE_KEYWORD);
|
|
suggestions.firstWhere(
|
|
(CompletionSuggestion suggestion) =>
|
|
suggestion.kind != CompletionSuggestionKind.KEYWORD, orElse: () {
|
|
fail('Expected suggestions other than keyword suggestions');
|
|
});
|
|
}
|
|
|
|
Future<void> test_catch3() async {
|
|
addTestFile('main() {try {} catch (e) {} finally {} ^}');
|
|
await getSuggestions();
|
|
assertNoResult('on');
|
|
assertNoResult('catch');
|
|
assertNoResult('finally');
|
|
assertHasResult(CompletionSuggestionKind.KEYWORD, 'for',
|
|
relevance: DART_RELEVANCE_KEYWORD);
|
|
suggestions.firstWhere(
|
|
(CompletionSuggestion suggestion) =>
|
|
suggestion.kind != CompletionSuggestionKind.KEYWORD, orElse: () {
|
|
fail('Expected suggestions other than keyword suggestions');
|
|
});
|
|
}
|
|
|
|
Future<void> test_catch4() async {
|
|
addTestFile('main() {try {} finally {} ^}');
|
|
await getSuggestions();
|
|
assertNoResult('on');
|
|
assertNoResult('catch');
|
|
assertNoResult('finally');
|
|
assertHasResult(CompletionSuggestionKind.KEYWORD, 'for',
|
|
relevance: DART_RELEVANCE_KEYWORD);
|
|
suggestions.firstWhere(
|
|
(CompletionSuggestion suggestion) =>
|
|
suggestion.kind != CompletionSuggestionKind.KEYWORD, orElse: () {
|
|
fail('Expected suggestions other than keyword suggestions');
|
|
});
|
|
}
|
|
|
|
Future<void> test_catch5() async {
|
|
addTestFile('main() {try {} ^ finally {}}');
|
|
await getSuggestions();
|
|
assertHasResult(CompletionSuggestionKind.KEYWORD, 'on',
|
|
relevance: DART_RELEVANCE_KEYWORD);
|
|
assertHasResult(CompletionSuggestionKind.KEYWORD, 'catch',
|
|
relevance: DART_RELEVANCE_KEYWORD);
|
|
expect(suggestions, hasLength(2));
|
|
}
|
|
|
|
Future<void> test_constructor() async {
|
|
addTestFile('class A {bool foo; A() : ^;}');
|
|
await getSuggestions();
|
|
assertHasResult(CompletionSuggestionKind.KEYWORD, 'super',
|
|
relevance: DART_RELEVANCE_KEYWORD);
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'foo',
|
|
relevance: DART_RELEVANCE_LOCAL_FIELD);
|
|
}
|
|
|
|
Future<void> test_constructor2() async {
|
|
addTestFile('class A {bool foo; A() : s^;}');
|
|
await getSuggestions();
|
|
assertHasResult(CompletionSuggestionKind.KEYWORD, 'super',
|
|
relevance: DART_RELEVANCE_KEYWORD);
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'foo',
|
|
relevance: DART_RELEVANCE_LOCAL_FIELD);
|
|
}
|
|
|
|
Future<void> test_constructor3() async {
|
|
addTestFile('class A {bool foo; A() : a=7,^;}');
|
|
await getSuggestions();
|
|
assertHasResult(CompletionSuggestionKind.KEYWORD, 'super',
|
|
relevance: DART_RELEVANCE_KEYWORD);
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'foo',
|
|
relevance: DART_RELEVANCE_LOCAL_FIELD);
|
|
}
|
|
|
|
Future<void> test_constructor4() async {
|
|
addTestFile('class A {bool foo; A() : a=7,s^;}');
|
|
await getSuggestions();
|
|
assertHasResult(CompletionSuggestionKind.KEYWORD, 'super',
|
|
relevance: DART_RELEVANCE_KEYWORD);
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'foo',
|
|
relevance: DART_RELEVANCE_LOCAL_FIELD);
|
|
}
|
|
|
|
Future<void> test_constructor5() async {
|
|
addTestFile('class A {bool foo; A() : a=7,s^}');
|
|
await getSuggestions();
|
|
assertHasResult(CompletionSuggestionKind.KEYWORD, 'super',
|
|
relevance: DART_RELEVANCE_KEYWORD);
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'foo',
|
|
relevance: DART_RELEVANCE_LOCAL_FIELD);
|
|
}
|
|
|
|
Future<void> test_constructor6() async {
|
|
addTestFile('class A {bool foo; A() : a=7,^ void bar() {}}');
|
|
await getSuggestions();
|
|
assertHasResult(CompletionSuggestionKind.KEYWORD, 'super',
|
|
relevance: DART_RELEVANCE_KEYWORD);
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'foo',
|
|
relevance: DART_RELEVANCE_LOCAL_FIELD);
|
|
}
|
|
|
|
Future<void> test_html() {
|
|
//
|
|
// We no longer support the analysis of non-dart files.
|
|
//
|
|
testFile = convertPath('/project/web/test.html');
|
|
addTestFile('''
|
|
<html>^</html>
|
|
''');
|
|
return getSuggestions().then((_) {
|
|
expect(replacementOffset, equals(completionOffset));
|
|
expect(replacementLength, equals(0));
|
|
expect(suggestions, hasLength(0));
|
|
});
|
|
}
|
|
|
|
Future<void> test_import_uri_with_trailing() {
|
|
final filePath = '/project/bin/testA.dart';
|
|
final incompleteImportText = toUriStr('/project/bin/t');
|
|
newFile(filePath, content: 'library libA;');
|
|
addTestFile('''
|
|
import "$incompleteImportText^.dart";
|
|
main() {}''');
|
|
return getSuggestions().then((_) {
|
|
expect(replacementOffset,
|
|
equals(completionOffset - incompleteImportText.length));
|
|
expect(replacementLength, equals(5 + incompleteImportText.length));
|
|
assertHasResult(CompletionSuggestionKind.IMPORT, toUriStr(filePath));
|
|
assertNoResult('test');
|
|
});
|
|
}
|
|
|
|
Future<void> test_imports() {
|
|
addTestFile('''
|
|
import 'dart:html';
|
|
main() {^}
|
|
''');
|
|
return getSuggestions().then((_) {
|
|
expect(replacementOffset, equals(completionOffset));
|
|
expect(replacementLength, equals(0));
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'Object',
|
|
elementKind: ElementKind.CLASS);
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'HtmlElement',
|
|
elementKind: ElementKind.CLASS);
|
|
assertNoResult('test');
|
|
});
|
|
}
|
|
|
|
Future<void> test_imports_aborted_new_request() async {
|
|
addTestFile('''
|
|
class foo { }
|
|
c^''');
|
|
|
|
// Make a request for suggestions
|
|
var request1 = CompletionGetSuggestionsParams(testFile, completionOffset)
|
|
.toRequest('7');
|
|
var responseFuture1 = waitResponse(request1);
|
|
|
|
// Make another request before the first request completes
|
|
var request2 = CompletionGetSuggestionsParams(testFile, completionOffset)
|
|
.toRequest('8');
|
|
var responseFuture2 = waitResponse(request2);
|
|
|
|
// Await first response
|
|
var response1 = await responseFuture1;
|
|
var result1 = CompletionGetSuggestionsResult.fromResponse(response1);
|
|
assertValidId(result1.id);
|
|
|
|
// Await second response
|
|
var response2 = await responseFuture2;
|
|
var result2 = CompletionGetSuggestionsResult.fromResponse(response2);
|
|
assertValidId(result2.id);
|
|
|
|
// Wait for all processing to be complete
|
|
await analysisHandler.server.analysisDriverScheduler.waitForIdle();
|
|
await pumpEventQueue();
|
|
|
|
// Assert that first request has been aborted
|
|
expect(allSuggestions[result1.id], hasLength(0));
|
|
|
|
// Assert valid results for the second request
|
|
expect(allSuggestions[result2.id], same(suggestions));
|
|
assertHasResult(CompletionSuggestionKind.KEYWORD, 'class',
|
|
relevance: DART_RELEVANCE_HIGH);
|
|
}
|
|
|
|
@failingTest
|
|
Future<void> test_imports_aborted_source_changed() async {
|
|
// TODO(brianwilkerson) Figure out whether this test makes sense when
|
|
// running the new driver. It waits for an initial empty notification then
|
|
// waits for a new notification. But I think that under the driver we only
|
|
// ever send one notification.
|
|
addTestFile('''
|
|
class foo { }
|
|
c^''');
|
|
|
|
// Make a request for suggestions
|
|
var request = CompletionGetSuggestionsParams(testFile, completionOffset)
|
|
.toRequest('0');
|
|
var responseFuture = waitResponse(request);
|
|
|
|
// Simulate user deleting text after request but before suggestions returned
|
|
server.updateContent('uc1', {testFile: AddContentOverlay(testCode)});
|
|
server.updateContent('uc2', {
|
|
testFile: ChangeContentOverlay([SourceEdit(completionOffset - 1, 1, '')])
|
|
});
|
|
|
|
// Await a response
|
|
var response = await responseFuture;
|
|
completionId = response.id;
|
|
assertValidId(completionId);
|
|
|
|
// Wait for all processing to be complete
|
|
await analysisHandler.server.analysisDriverScheduler.waitForIdle();
|
|
await pumpEventQueue();
|
|
|
|
// Assert that request has been aborted
|
|
expect(suggestionsDone, isTrue);
|
|
expect(suggestions, hasLength(0));
|
|
}
|
|
|
|
Future<void> test_imports_incremental() async {
|
|
addTestFile('''library foo;
|
|
e^
|
|
import "dart:async";
|
|
import "package:foo/foo.dart";
|
|
class foo { }''');
|
|
await waitForTasksFinished();
|
|
server.updateContent('uc1', {testFile: AddContentOverlay(testCode)});
|
|
server.updateContent('uc2', {
|
|
testFile: ChangeContentOverlay([SourceEdit(completionOffset, 0, 'xp')])
|
|
});
|
|
completionOffset += 2;
|
|
await getSuggestions();
|
|
expect(replacementOffset, completionOffset - 3);
|
|
expect(replacementLength, 3);
|
|
assertHasResult(CompletionSuggestionKind.KEYWORD, 'export \'\';',
|
|
selectionOffset: 8, relevance: DART_RELEVANCE_HIGH);
|
|
assertHasResult(CompletionSuggestionKind.KEYWORD, 'import \'\';',
|
|
selectionOffset: 8, relevance: DART_RELEVANCE_HIGH);
|
|
assertNoResult('extends');
|
|
assertNoResult('library');
|
|
}
|
|
|
|
Future<void> test_imports_partial() async {
|
|
addTestFile('''^
|
|
import "package:foo/foo.dart";
|
|
import "package:bar/bar.dart";
|
|
class Baz { }''');
|
|
|
|
// Wait for analysis then edit the content
|
|
await waitForTasksFinished();
|
|
var revisedContent = testCode.substring(0, completionOffset) +
|
|
'i' +
|
|
testCode.substring(completionOffset);
|
|
++completionOffset;
|
|
server.handleRequest(AnalysisUpdateContentParams(
|
|
{testFile: AddContentOverlay(revisedContent)}).toRequest('add1'));
|
|
|
|
// Request code completion immediately after edit
|
|
var response = await waitResponse(
|
|
CompletionGetSuggestionsParams(testFile, completionOffset)
|
|
.toRequest('0'));
|
|
completionId = response.id;
|
|
assertValidId(completionId);
|
|
await waitForTasksFinished();
|
|
// wait for response to arrive
|
|
// because although the analysis is complete (waitForTasksFinished)
|
|
// the response may not yet have been processed
|
|
while (replacementOffset == null) {
|
|
await Future.delayed(Duration(milliseconds: 5));
|
|
}
|
|
expect(replacementOffset, completionOffset - 1);
|
|
expect(replacementLength, 1);
|
|
assertHasResult(CompletionSuggestionKind.KEYWORD, 'library',
|
|
relevance: DART_RELEVANCE_HIGH);
|
|
assertHasResult(CompletionSuggestionKind.KEYWORD, 'import \'\';',
|
|
selectionOffset: 8, relevance: DART_RELEVANCE_HIGH);
|
|
assertHasResult(CompletionSuggestionKind.KEYWORD, 'export \'\';',
|
|
selectionOffset: 8, relevance: DART_RELEVANCE_HIGH);
|
|
assertHasResult(CompletionSuggestionKind.KEYWORD, 'part \'\';',
|
|
selectionOffset: 6, relevance: DART_RELEVANCE_HIGH);
|
|
assertNoResult('extends');
|
|
}
|
|
|
|
Future<void> test_imports_prefixed() {
|
|
addTestFile('''
|
|
import 'dart:html' as foo;
|
|
main() {^}
|
|
''');
|
|
return getSuggestions().then((_) {
|
|
expect(replacementOffset, equals(completionOffset));
|
|
expect(replacementLength, equals(0));
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'Object',
|
|
elementKind: ElementKind.CLASS);
|
|
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'foo');
|
|
assertNoResult('HtmlElement');
|
|
assertNoResult('test');
|
|
});
|
|
}
|
|
|
|
Future<void> test_imports_prefixed2() {
|
|
addTestFile('''
|
|
import 'dart:html' as foo;
|
|
main() {foo.^}
|
|
''');
|
|
return getSuggestions().then((_) {
|
|
expect(replacementOffset, equals(completionOffset));
|
|
expect(replacementLength, equals(0));
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'HtmlElement');
|
|
assertNoResult('test');
|
|
});
|
|
}
|
|
|
|
Future<void> test_inComment_block_beforeNode() async {
|
|
addTestFile('''
|
|
main(aaa, bbb) {
|
|
/* text ^ */
|
|
print(42);
|
|
}
|
|
''');
|
|
await getSuggestions();
|
|
expect(suggestions, isEmpty);
|
|
}
|
|
|
|
Future<void> test_inComment_endOfLine_beforeNode() async {
|
|
addTestFile('''
|
|
main(aaa, bbb) {
|
|
// text ^
|
|
print(42);
|
|
}
|
|
''');
|
|
await getSuggestions();
|
|
expect(suggestions, isEmpty);
|
|
}
|
|
|
|
Future<void> test_inComment_endOfLine_beforeToken() async {
|
|
addTestFile('''
|
|
main(aaa, bbb) {
|
|
// text ^
|
|
}
|
|
''');
|
|
await getSuggestions();
|
|
expect(suggestions, isEmpty);
|
|
}
|
|
|
|
Future<void> test_inDartDoc1() async {
|
|
addTestFile('''
|
|
/// ^
|
|
main(aaa, bbb) {}
|
|
''');
|
|
await getSuggestions();
|
|
expect(suggestions, isEmpty);
|
|
}
|
|
|
|
Future<void> test_inDartDoc2() async {
|
|
addTestFile('''
|
|
/// Some text^
|
|
main(aaa, bbb) {}
|
|
''');
|
|
await getSuggestions();
|
|
expect(suggestions, isEmpty);
|
|
}
|
|
|
|
Future<void> test_inDartDoc_reference1() async {
|
|
newFile('/testA.dart', content: '''
|
|
part of libA;
|
|
foo(bar) => 0;''');
|
|
addTestFile('''
|
|
library libA;
|
|
part "${toUriStr('/testA.dart')}";
|
|
import "dart:math";
|
|
/// The [^]
|
|
main(aaa, bbb) {}
|
|
''');
|
|
await getSuggestions();
|
|
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'main',
|
|
relevance: DART_RELEVANCE_LOCAL_FUNCTION);
|
|
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'foo',
|
|
relevance: DART_RELEVANCE_LOCAL_FUNCTION);
|
|
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'min');
|
|
}
|
|
|
|
Future<void> test_inDartDoc_reference2() async {
|
|
addTestFile('''
|
|
/// The [m^]
|
|
main(aaa, bbb) {}
|
|
''');
|
|
await getSuggestions();
|
|
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'main',
|
|
relevance: DART_RELEVANCE_LOCAL_FUNCTION);
|
|
}
|
|
|
|
Future<void> test_inherited() {
|
|
addTestFile('''
|
|
class A {
|
|
m() {}
|
|
}
|
|
class B extends A {
|
|
x() {^}
|
|
}
|
|
''');
|
|
return getSuggestions().then((_) {
|
|
expect(replacementOffset, equals(completionOffset));
|
|
expect(replacementLength, equals(0));
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'm',
|
|
relevance: DART_RELEVANCE_LOCAL_METHOD);
|
|
});
|
|
}
|
|
|
|
Future<void> test_invalidFilePathFormat_notAbsolute() async {
|
|
var request = CompletionGetSuggestionsParams('test.dart', 0).toRequest('0');
|
|
var response = await waitResponse(request);
|
|
expect(
|
|
response,
|
|
isResponseFailure('0', RequestErrorCode.INVALID_FILE_PATH_FORMAT),
|
|
);
|
|
}
|
|
|
|
Future<void> test_invalidFilePathFormat_notNormalized() async {
|
|
var request =
|
|
CompletionGetSuggestionsParams(convertPath('/foo/../bar/test.dart'), 0)
|
|
.toRequest('0');
|
|
var response = await waitResponse(request);
|
|
expect(
|
|
response,
|
|
isResponseFailure('0', RequestErrorCode.INVALID_FILE_PATH_FORMAT),
|
|
);
|
|
}
|
|
|
|
Future<void> test_invocation() {
|
|
addTestFile('class A {b() {}} main() {A a; a.^}');
|
|
return getSuggestions().then((_) {
|
|
expect(replacementOffset, equals(completionOffset));
|
|
expect(replacementLength, equals(0));
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'b',
|
|
relevance: DART_RELEVANCE_LOCAL_METHOD);
|
|
});
|
|
}
|
|
|
|
Future<void> test_invocation_sdk_relevancy_off() {
|
|
var originalSorter = DartCompletionManager.contributionSorter;
|
|
var mockSorter = MockRelevancySorter();
|
|
DartCompletionManager.contributionSorter = mockSorter;
|
|
addTestFile('main() {Map m; m.^}');
|
|
return getSuggestions().then((_) {
|
|
// Assert that the CommonUsageComputer has been replaced
|
|
expect(suggestions.any((s) => s.relevance == DART_RELEVANCE_COMMON_USAGE),
|
|
isFalse);
|
|
DartCompletionManager.contributionSorter = originalSorter;
|
|
mockSorter.enabled = false;
|
|
});
|
|
}
|
|
|
|
Future<void> test_invocation_sdk_relevancy_on() {
|
|
if (!server.options.useNewRelevance) {
|
|
addTestFile('main() {Map m; m.^}');
|
|
return getSuggestions().then((_) {
|
|
// Assert that the CommonUsageComputer is working
|
|
expect(
|
|
suggestions.any((s) => s.relevance == DART_RELEVANCE_COMMON_USAGE),
|
|
isTrue);
|
|
});
|
|
}
|
|
return Future.value(null);
|
|
}
|
|
|
|
Future<void> test_invocation_withTrailingStmt() {
|
|
addTestFile('class A {b() {}} main() {A a; a.^ int x = 7;}');
|
|
return getSuggestions().then((_) {
|
|
expect(replacementOffset, equals(completionOffset));
|
|
expect(replacementLength, equals(0));
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'b',
|
|
relevance: DART_RELEVANCE_LOCAL_METHOD);
|
|
});
|
|
}
|
|
|
|
Future<void> test_is_asPrefixedIdentifierStart() async {
|
|
addTestFile('''
|
|
class A { var isVisible;}
|
|
main(A p) { var v1 = p.is^; }''');
|
|
await getSuggestions();
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'isVisible',
|
|
relevance: DART_RELEVANCE_LOCAL_FIELD);
|
|
}
|
|
|
|
Future<void> test_keyword() {
|
|
addTestFile('library A; cl^');
|
|
return getSuggestions().then((_) {
|
|
expect(replacementOffset, equals(completionOffset - 2));
|
|
expect(replacementLength, equals(2));
|
|
assertHasResult(CompletionSuggestionKind.KEYWORD, 'export \'\';',
|
|
selectionOffset: 8, relevance: DART_RELEVANCE_HIGH);
|
|
assertHasResult(CompletionSuggestionKind.KEYWORD, 'class',
|
|
relevance: DART_RELEVANCE_HIGH);
|
|
});
|
|
}
|
|
|
|
Future<void> test_local_implicitCreation() async {
|
|
addTestFile('''
|
|
class A {
|
|
A();
|
|
A.named();
|
|
}
|
|
main() {
|
|
^
|
|
}
|
|
''');
|
|
await getSuggestions();
|
|
|
|
expect(replacementOffset, equals(completionOffset));
|
|
expect(replacementLength, equals(0));
|
|
|
|
// The class is suggested.
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'A',
|
|
elementKind: ElementKind.CLASS);
|
|
|
|
// Both constructors - default and named, are suggested.
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'A',
|
|
elementKind: ElementKind.CONSTRUCTOR);
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'A.named',
|
|
elementKind: ElementKind.CONSTRUCTOR);
|
|
}
|
|
|
|
Future<void> test_local_named_constructor() {
|
|
addTestFile('class A {A.c(); x() {new A.^}}');
|
|
return getSuggestions().then((_) {
|
|
expect(replacementOffset, equals(completionOffset));
|
|
expect(replacementLength, equals(0));
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'c');
|
|
assertNoResult('A');
|
|
});
|
|
}
|
|
|
|
Future<void> test_local_override() {
|
|
newFile('/project/bin/a.dart', content: 'class A {m() {}}');
|
|
addTestFile('''
|
|
import 'a.dart';
|
|
class B extends A {
|
|
m() {}
|
|
x() {^}
|
|
}
|
|
''');
|
|
return getSuggestions().then((_) {
|
|
expect(replacementOffset, equals(completionOffset));
|
|
expect(replacementLength, equals(0));
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'm',
|
|
relevance: DART_RELEVANCE_LOCAL_METHOD);
|
|
});
|
|
}
|
|
|
|
Future<void> test_local_shadowClass() async {
|
|
addTestFile('''
|
|
class A {
|
|
A();
|
|
A.named();
|
|
}
|
|
main() {
|
|
int A = 0;
|
|
^
|
|
}
|
|
''');
|
|
await getSuggestions();
|
|
|
|
expect(replacementOffset, equals(completionOffset));
|
|
expect(replacementLength, equals(0));
|
|
|
|
// The class is suggested.
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'A',
|
|
relevance: DART_RELEVANCE_LOCAL_VARIABLE);
|
|
|
|
// Class and all its constructors are shadowed by the local variable.
|
|
assertNoResult('A', elementKind: ElementKind.CLASS);
|
|
assertNoResult('A', elementKind: ElementKind.CONSTRUCTOR);
|
|
assertNoResult('A.named', elementKind: ElementKind.CONSTRUCTOR);
|
|
}
|
|
|
|
Future<void> test_locals() {
|
|
addTestFile('class A {var a; x() {var b;^}} class DateTime { }');
|
|
return getSuggestions().then((_) {
|
|
expect(replacementOffset, equals(completionOffset));
|
|
expect(replacementLength, equals(0));
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'A',
|
|
elementKind: ElementKind.CLASS);
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'a',
|
|
relevance: DART_RELEVANCE_LOCAL_FIELD);
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'b',
|
|
relevance: DART_RELEVANCE_LOCAL_VARIABLE);
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'x',
|
|
relevance: DART_RELEVANCE_LOCAL_METHOD);
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'DateTime',
|
|
elementKind: ElementKind.CLASS);
|
|
});
|
|
}
|
|
|
|
Future<void> test_offset_past_eof() async {
|
|
addTestFile('main() { }', offset: 300);
|
|
var request = CompletionGetSuggestionsParams(testFile, completionOffset)
|
|
.toRequest('0');
|
|
var response = await waitResponse(request);
|
|
expect(response.id, '0');
|
|
expect(response.error.code, RequestErrorCode.INVALID_PARAMETER);
|
|
}
|
|
|
|
Future<void> test_overrides() {
|
|
newFile('/project/bin/a.dart', content: 'class A {m() {}}');
|
|
addTestFile('''
|
|
import 'a.dart';
|
|
class B extends A {m() {^}}
|
|
''');
|
|
return getSuggestions().then((_) {
|
|
expect(replacementOffset, equals(completionOffset));
|
|
expect(replacementLength, equals(0));
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'm',
|
|
relevance: DART_RELEVANCE_LOCAL_METHOD);
|
|
});
|
|
}
|
|
|
|
Future<void> test_partFile() {
|
|
newFile('/project/bin/a.dart', content: '''
|
|
library libA;
|
|
import 'dart:html';
|
|
part 'test.dart';
|
|
class A { }
|
|
''');
|
|
addTestFile('''
|
|
part of libA;
|
|
main() {^}''');
|
|
return getSuggestions().then((_) {
|
|
expect(replacementOffset, equals(completionOffset));
|
|
expect(replacementLength, equals(0));
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'Object',
|
|
elementKind: ElementKind.CLASS);
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'HtmlElement',
|
|
elementKind: ElementKind.CLASS);
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'A',
|
|
elementKind: ElementKind.CLASS);
|
|
assertNoResult('test');
|
|
});
|
|
}
|
|
|
|
Future<void> test_partFile2() {
|
|
newFile('/project/bin/a.dart', content: '''
|
|
part of libA;
|
|
class A { }''');
|
|
addTestFile('''
|
|
library libA;
|
|
part "a.dart";
|
|
import 'dart:html';
|
|
main() {^}
|
|
''');
|
|
return getSuggestions().then((_) {
|
|
expect(replacementOffset, equals(completionOffset));
|
|
expect(replacementLength, equals(0));
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'Object',
|
|
elementKind: ElementKind.CLASS);
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'HtmlElement',
|
|
elementKind: ElementKind.CLASS);
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'A',
|
|
elementKind: ElementKind.CLASS);
|
|
assertNoResult('test');
|
|
});
|
|
}
|
|
|
|
Future<void> test_sentToPlugins() async {
|
|
addTestFile('''
|
|
void main() {
|
|
^
|
|
}
|
|
''');
|
|
PluginInfo info = DiscoveredPluginInfo('a', 'b', 'c', null, null);
|
|
var result = plugin.CompletionGetSuggestionsResult(
|
|
testFile.indexOf('^'), 0, <CompletionSuggestion>[
|
|
CompletionSuggestion(CompletionSuggestionKind.IDENTIFIER,
|
|
DART_RELEVANCE_DEFAULT, 'plugin completion', 3, 0, false, false)
|
|
]);
|
|
pluginManager.broadcastResults = <PluginInfo, Future<plugin.Response>>{
|
|
info: Future.value(result.toResponse('-', 1))
|
|
};
|
|
await getSuggestions();
|
|
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'plugin completion',
|
|
selectionOffset: 3);
|
|
}
|
|
|
|
Future<void> test_simple() {
|
|
addTestFile('''
|
|
void main() {
|
|
^
|
|
}
|
|
''');
|
|
return getSuggestions().then((_) {
|
|
expect(replacementOffset, equals(completionOffset));
|
|
expect(replacementLength, equals(0));
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'Object',
|
|
elementKind: ElementKind.CLASS);
|
|
assertNoResult('HtmlElement');
|
|
assertNoResult('test');
|
|
});
|
|
}
|
|
|
|
Future<void> test_static() async {
|
|
addTestFile('class A {static b() {} c() {}} main() {A.^}');
|
|
await getSuggestions();
|
|
expect(replacementOffset, equals(completionOffset));
|
|
expect(replacementLength, equals(0));
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'b',
|
|
relevance: DART_RELEVANCE_INHERITED_METHOD);
|
|
assertNoResult('c');
|
|
}
|
|
|
|
Future<void> test_topLevel() {
|
|
addTestFile('''
|
|
typedef foo();
|
|
var test = '';
|
|
main() {tes^t}
|
|
''');
|
|
return getSuggestions().then((_) {
|
|
expect(replacementOffset, equals(completionOffset - 3));
|
|
expect(replacementLength, equals(4));
|
|
// Suggestions based upon imported elements are partially filtered
|
|
//assertHasResult(CompletionSuggestionKind.INVOCATION, 'Object');
|
|
assertHasResult(CompletionSuggestionKind.INVOCATION, 'test',
|
|
relevance: DART_RELEVANCE_LOCAL_TOP_LEVEL_VARIABLE);
|
|
assertNoResult('HtmlElement');
|
|
});
|
|
}
|
|
}
|
|
|
|
@reflectiveTest
|
|
class CompletionDomainHandlerListTokenDetailsTest
|
|
extends AbstractCompletionDomainTest {
|
|
String testFileUri;
|
|
|
|
Future<void> expectTokens(String content, List<TokenDetails> expected) async {
|
|
newFile(testFile, content: content);
|
|
var request = CompletionListTokenDetailsParams(testFile).toRequest('0');
|
|
var response = await waitResponse(request);
|
|
List<Map<String, dynamic>> tokens = response.result['tokens'];
|
|
_compareTokens(tokens, expected);
|
|
}
|
|
|
|
@override
|
|
void setUp() {
|
|
super.setUp();
|
|
testFileUri = toUriStr(testFile);
|
|
}
|
|
|
|
Future<void> test_classDeclaration() async {
|
|
await expectTokens('''
|
|
class A {}
|
|
class B extends A {}
|
|
class C implements B {}
|
|
class D with C {}
|
|
''', [
|
|
token('class', 0, null, null),
|
|
token('A', 6, 'Type',
|
|
['declaration']), //token('A', 6, 'dart:core;Type', ['declaration']),
|
|
token('{', 8, null, null),
|
|
token('}', 9, null, null),
|
|
token('class', 11, null, null),
|
|
token('B', 17, 'Type',
|
|
['declaration']), //token('B', 17, 'dart:core;Type', ['declaration']),
|
|
token('extends', 19, null, null),
|
|
token('A', 27, 'dart:core;Type<A>', [
|
|
'reference'
|
|
]), //token('A', 27, 'dart:core;Type<$testFileUri;A>', ['reference']),
|
|
token('{', 29, null, null),
|
|
token('}', 30, null, null),
|
|
token('class', 32, null, null),
|
|
token('C', 38, 'Type',
|
|
['declaration']), //token('C', 38, 'dart:core;Type', ['declaration']),
|
|
token('implements', 40, null, null),
|
|
token('B', 51, 'dart:core;Type<B>', [
|
|
'reference'
|
|
]), //token('B', 51, 'dart:core;Type<$testFileUri;B>', ['reference']),
|
|
token('{', 53, null, null),
|
|
token('}', 54, null, null),
|
|
token('class', 56, null, null),
|
|
token('D', 62, 'Type',
|
|
['declaration']), //token('D', 62, 'dart:core;Type', ['declaration']),
|
|
token('with', 64, null, null),
|
|
token('C', 69, 'dart:core;Type<C>', [
|
|
'reference'
|
|
]), //token('C', 69, 'dart:core;Type<$testFileUri;C>', ['reference']),
|
|
token('{', 71, null, null),
|
|
token('}', 72, null, null),
|
|
]);
|
|
}
|
|
|
|
Future<void> test_genericType() async {
|
|
await expectTokens('''
|
|
List<int> x = null;
|
|
''', [
|
|
token('List', 0, 'dart:core;Type<List>', [
|
|
'reference'
|
|
]), //token('List', 0, 'dart:core;Type<dart:core;List>', ['reference']),
|
|
token('<', 4, null, null),
|
|
token('int', 5, 'dart:core;Type<int>', [
|
|
'reference'
|
|
]), //token('int', 5, 'dart:core;Type<dart:core;int>', ['reference']),
|
|
token('>', 8, null, null),
|
|
token('x', 10, 'List',
|
|
['declaration']), //token('x', 10, 'dart:core;List', ['declaration']),
|
|
token('=', 12, null, null),
|
|
token('null', 14, null, null),
|
|
token(';', 18, null, null),
|
|
]);
|
|
}
|
|
|
|
Future<void> test_getterInvocation() async {
|
|
await expectTokens('''
|
|
var x = 'a'.length;
|
|
''', [
|
|
token('var', 0, null, null),
|
|
token('x', 4, 'int',
|
|
['declaration']), //token('x', 4, 'dart:core;int', ['declaration']),
|
|
token('=', 6, null, null),
|
|
token("'a'", 8, 'String',
|
|
null), //token("'a'", 8, 'dart:core;String', null),
|
|
token('.', 11, null, null),
|
|
token('length', 12, 'int',
|
|
['reference']), //token('length', 12, 'dart:core;int', ['reference']),
|
|
token(';', 18, null, null),
|
|
]);
|
|
}
|
|
|
|
Future<void> test_literal_bool() async {
|
|
await expectTokens('''
|
|
var x = true;
|
|
''', [
|
|
token('var', 0, null, null),
|
|
token('x', 4, 'bool',
|
|
['declaration']), //token('x', 4, 'dart:core;bool', ['declaration']),
|
|
token('=', 6, null, null),
|
|
token(
|
|
'true', 8, 'bool', null), //token('true', 8, 'dart:core;bool', null),
|
|
token(';', 12, null, null),
|
|
]);
|
|
}
|
|
|
|
Future<void> test_literal_double() async {
|
|
await expectTokens('''
|
|
var x = 3.4;
|
|
''', [
|
|
token('var', 0, null, null),
|
|
token('x', 4, 'double', [
|
|
'declaration'
|
|
]), //token('x', 4, 'dart:core;double', ['declaration']),
|
|
token('=', 6, null, null),
|
|
token('3.4', 8, 'double',
|
|
null), //token('3.4', 8, 'dart:core;double', null),
|
|
token(';', 11, null, null),
|
|
]);
|
|
}
|
|
|
|
Future<void> test_literal_int() async {
|
|
await expectTokens('''
|
|
var x = 7;
|
|
''', [
|
|
token('var', 0, null, null),
|
|
token('x', 4, 'int',
|
|
['declaration']), //token('x', 4, 'dart:core;int', ['declaration']),
|
|
token('=', 6, null, null),
|
|
token('7', 8, 'int', null), //token('7', 8, 'dart:core;int', null),
|
|
token(';', 9, null, null),
|
|
]);
|
|
}
|
|
|
|
Future<void> test_literal_list() async {
|
|
await expectTokens('''
|
|
var x = <int>[];
|
|
''', [
|
|
token('var', 0, null, null),
|
|
token('x', 4, 'List',
|
|
['declaration']), //token('x', 4, 'dart:core;List', ['declaration']),
|
|
token('=', 6, null, null),
|
|
token('<', 8, null, null),
|
|
token('int', 9, 'dart:core;Type<int>', [
|
|
'reference'
|
|
]), //token("int", 9, 'dart:core;Type<dart:core;int>', ['reference']),
|
|
token('>', 12, null, null),
|
|
token('[', 13, null, null),
|
|
token(']', 14, null, null),
|
|
token(';', 15, null, null),
|
|
]);
|
|
}
|
|
|
|
Future<void> test_literal_map() async {
|
|
await expectTokens('''
|
|
var x = <int, int>{};
|
|
''', [
|
|
token('var', 0, null, null),
|
|
token('x', 4, 'Map',
|
|
['declaration']), //token('x', 4, 'dart:core;Map', ['declaration']),
|
|
token('=', 6, null, null),
|
|
token('<', 8, null, null),
|
|
token('int', 9, 'dart:core;Type<int>', [
|
|
'reference'
|
|
]), //token("int", 9, 'dart:core;Type<dart:core;int>', ['reference']),
|
|
// token(',', null, null),
|
|
token('int', 14, 'dart:core;Type<int>', [
|
|
'reference'
|
|
]), //token("int", 14, 'dart:core;Type<dart:core;int>', ['reference']),
|
|
token('>', 17, null, null),
|
|
token('{', 18, null, null),
|
|
token('}', 19, null, null),
|
|
token(';', 20, null, null),
|
|
]);
|
|
}
|
|
|
|
Future<void> test_literal_null() async {
|
|
await expectTokens('''
|
|
var x = null;
|
|
''', [
|
|
token('var', 0, null, null),
|
|
token('x', 4, 'dynamic', ['declaration']),
|
|
token('=', 6, null, null),
|
|
token('null', 8, null, null),
|
|
token(';', 12, null, null),
|
|
]);
|
|
}
|
|
|
|
Future<void> test_literal_set() async {
|
|
await expectTokens('''
|
|
var x = <int>{};
|
|
''', [
|
|
token('var', 0, null, null),
|
|
token('x', 4, 'Set',
|
|
['declaration']), //token('x', 4, 'dart:core;Set', ['declaration']),
|
|
token('=', 6, null, null),
|
|
token('<', 8, null, null),
|
|
token('int', 9, 'dart:core;Type<int>', [
|
|
'reference'
|
|
]), //token("int", 9, 'dart:core;Type<dart:core;int>', ['reference']),
|
|
token('>', 12, null, null),
|
|
token('{', 13, null, null),
|
|
token('}', 14, null, null),
|
|
token(';', 15, null, null),
|
|
]);
|
|
}
|
|
|
|
Future<void> test_literal_string() async {
|
|
await expectTokens('''
|
|
var x = 'a';
|
|
''', [
|
|
token('var', 0, null, null),
|
|
token('x', 4, 'String', [
|
|
'declaration'
|
|
]), //token('x', 4, 'dart:core;String', ['declaration']),
|
|
token('=', 6, null, null),
|
|
token("'a'", 8, 'String',
|
|
null), //token("'a'", 8, 'dart:core;String', null),
|
|
token(';', 11, null, null),
|
|
]);
|
|
}
|
|
|
|
Future<void> test_methodDeclaration() async {
|
|
await expectTokens('''
|
|
class A {
|
|
String c(int x, int y) {}
|
|
}
|
|
''', [
|
|
token('class', 0, null, null),
|
|
token('A', 6, 'Type', ['declaration']),
|
|
//token('A', 6, 'dart:core;Type', ['declaration']),
|
|
token('{', 8, null, null),
|
|
token('String', 12, 'dart:core;Type<String>', ['reference']),
|
|
//token('String', 12, 'dart:core;Type<dart:core;String>', ['reference']),
|
|
token('c', 19, 'String Function(int, int)',
|
|
//'dart:core;String Function(dart:core;int, dart:core;int)',
|
|
['declaration']),
|
|
token('(', 20, null, null),
|
|
token('int', 21, 'dart:core;Type<int>', ['reference']),
|
|
//token('int', 21, 'dart:core;Type<dart:core;int>', ['reference']),
|
|
token('x', 25, 'int', ['declaration']),
|
|
//token('x', 25, 'dart:core;int', ['declaration']),
|
|
// token(',', null, null),
|
|
token('int', 28, 'dart:core;Type<int>', ['reference']),
|
|
//token('int', 28, 'dart:core;Type<dart:core;int>', ['reference']),
|
|
token('y', 32, 'int', ['declaration']),
|
|
//token('y', 32, 'dart:core;int', ['declaration']),
|
|
token(')', 33, null, null),
|
|
token('{', 35, null, null),
|
|
token('}', 36, null, null),
|
|
token('}', 38, null, null),
|
|
]);
|
|
}
|
|
|
|
Future<void> test_methodInvocation() async {
|
|
await expectTokens('''
|
|
var x = 'radar'.indexOf('r', 1);
|
|
''', [
|
|
token('var', 0, null, null),
|
|
token('x', 4, 'int',
|
|
['declaration']), //token('x', 4, 'dart:core;int', ['declaration']),
|
|
token('=', 6, null, null),
|
|
token("'radar'", 8, 'String',
|
|
null), //token("'radar'", 8, 'dart:core;String', null),
|
|
token('.', 15, null, null),
|
|
token('indexOf', 16, 'int Function(Pattern, int)',
|
|
//'dart:core;int Function(dart:core;Pattern, dart:core;int)',
|
|
['reference']),
|
|
token('(', 23, null, null),
|
|
token("'r'", 24, 'String',
|
|
null), //token("'r'", 24, 'dart:core;String', null),
|
|
// token(',', null, null),
|
|
token('1', 29, 'int', null), //token('1', 29, 'dart:core;int', null),
|
|
token(')', 30, null, null),
|
|
token(';', 31, null, null),
|
|
]);
|
|
}
|
|
|
|
Future<void> test_mixinDeclaration() async {
|
|
await expectTokens('''
|
|
class A {}
|
|
class B {}
|
|
mixin D on A implements B {}
|
|
''', [
|
|
token('class', 0, null, null),
|
|
token('A', 6, 'Type',
|
|
['declaration']), //token('A', 6, 'dart:core;Type', ['declaration']),
|
|
token('{', 8, null, null),
|
|
token('}', 9, null, null),
|
|
token('class', 11, null, null),
|
|
token('B', 17, 'Type',
|
|
['declaration']), //token('B', 17, 'dart:core;Type', ['declaration']),
|
|
token('{', 19, null, null),
|
|
token('}', 20, null, null),
|
|
token('mixin', 22, null, null),
|
|
token('D', 28, 'Type',
|
|
['declaration']), //token('D', 28, 'dart:core;Type', ['declaration']),
|
|
token('on', 30, null, null),
|
|
token('A', 33, 'dart:core;Type<A>', [
|
|
'reference'
|
|
]), //token('A', 33, 'dart:core;Type<$testFileUri;A>', ['reference']),
|
|
token('implements', 35, null, null),
|
|
token('B', 46, 'dart:core;Type<B>', [
|
|
'reference'
|
|
]), //token('B', 'dart:core;Type<$testFileUri;B>', ['reference']),
|
|
token('{', 48, null, null),
|
|
token('}', 49, null, null),
|
|
]);
|
|
}
|
|
|
|
Future<void> test_parameterReference() async {
|
|
await expectTokens('''
|
|
int f(int p) {
|
|
return p;
|
|
}
|
|
''', [
|
|
token('int', 0, 'dart:core;Type<int>', ['reference']),
|
|
//token('int', 0, 'dart:core;Type<dart:core;int>', ['reference']),
|
|
token('f', 4, 'int Function(int)', ['declaration']),
|
|
//token('f', 4, 'dart:core;int Function(dart:core;int)', ['declaration']),
|
|
token('(', 5, null, null),
|
|
token('int', 6, 'dart:core;Type<int>', ['reference']),
|
|
//token('int', 6, 'dart:core;Type<dart:core;int>', ['reference']),
|
|
token('p', 10, 'int', ['declaration']),
|
|
//token('p', 10, 'dart:core;int', ['declaration']),
|
|
token(')', 11, null, null),
|
|
token('{', 13, null, null),
|
|
token('return', 17, null, null),
|
|
token('p', 24, 'int', ['reference']),
|
|
//token('p', 24, 'dart:core;int', ['reference']),
|
|
token(';', 25, null, null),
|
|
token('}', 27, null, null),
|
|
]);
|
|
}
|
|
|
|
Future<void> test_topLevelVariable_withDocComment() async {
|
|
await expectTokens('''
|
|
/// Doc comment [x] with reference.
|
|
int x;
|
|
''', [
|
|
token('int', 36, 'dart:core;Type<int>', [
|
|
'reference'
|
|
]), //token('int', 36, 'dart:core;Type<dart:core;int>', ['reference']),
|
|
token('x', 40, 'int',
|
|
['declaration']), //token('x', 40, 'dart:core;int', ['declaration']),
|
|
token(';', 41, null, null),
|
|
]);
|
|
}
|
|
|
|
TokenDetails token(
|
|
String lexeme, int offset, String type, List<String> kinds) {
|
|
return TokenDetails(lexeme, offset, type: type, validElementKinds: kinds);
|
|
}
|
|
|
|
void _compareTokens(List<Map<String, dynamic>> actualTokens,
|
|
List<TokenDetails> expectedTokens) {
|
|
var length = expectedTokens.length;
|
|
expect(actualTokens, hasLength(length));
|
|
var errors = <String>[];
|
|
for (var i = 0; i < length; i++) {
|
|
var actual = actualTokens[i];
|
|
var expected = expectedTokens[i];
|
|
if (actual['lexeme'] != expected.lexeme) {
|
|
errors.add('Lexeme at $i: '
|
|
'expected "${expected.lexeme}", '
|
|
'actual "${actual['lexeme']}"');
|
|
}
|
|
if (actual['offset'] != expected.offset) {
|
|
errors.add('Offset at $i: ("${expected.lexeme}"): '
|
|
'expected "${expected.offset}", '
|
|
'actual "${actual['offset']}"');
|
|
}
|
|
if (actual['type'] != expected.type) {
|
|
errors.add('Type at $i ("${expected.lexeme}"): '
|
|
'expected "${expected.type}", '
|
|
'actual "${actual['type']}"');
|
|
}
|
|
if (_differentKinds(
|
|
actual['validElementKinds'], expected.validElementKinds)) {
|
|
errors.add('Kinds at $i ("${expected.lexeme}"): '
|
|
'expected "${expected.validElementKinds}", '
|
|
'actual "${actual['validElementKinds']}"');
|
|
}
|
|
}
|
|
expect(errors, isEmpty);
|
|
}
|
|
|
|
/// Return `true` if the two lists of kinds are different.
|
|
bool _differentKinds(List<String> actual, List<String> expected) {
|
|
if (actual == null) {
|
|
return expected != null;
|
|
} else if (expected == null) {
|
|
return true;
|
|
}
|
|
var expectedLength = expected.length;
|
|
if (actual.length != expectedLength) {
|
|
return true;
|
|
}
|
|
for (var i = 0; i < expectedLength; i++) {
|
|
if (actual[i] != expected[i]) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
class MockRelevancySorter implements DartContributionSorter {
|
|
bool enabled = true;
|
|
|
|
@override
|
|
Future sort(
|
|
CompletionRequest request, Iterable<CompletionSuggestion> suggestions) {
|
|
if (!enabled) {
|
|
throw 'unexpected sort';
|
|
}
|
|
return Future.value();
|
|
}
|
|
}
|