mirror of
https://github.com/dart-lang/sdk
synced 2024-09-19 20:51:50 +00:00
Code completion for record fields.
Change-Id: I03134b2c0c7fac2595c23b00f9d13b5e344d15a9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/256961 Commit-Queue: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
parent
3fd0d96e72
commit
cfde826cc0
|
@ -937,6 +937,36 @@ class SuggestionBuilder {
|
|||
);
|
||||
}
|
||||
|
||||
void suggestRecordField({
|
||||
required RecordTypeField field,
|
||||
required String name,
|
||||
}) {
|
||||
final type = field.type;
|
||||
final featureComputer = request.featureComputer;
|
||||
final contextType =
|
||||
featureComputer.contextTypeFeature(request.contextType, type);
|
||||
final relevance = _computeRelevance(
|
||||
contextType: contextType,
|
||||
);
|
||||
|
||||
final returnType = field.type.getDisplayString(
|
||||
withNullability: _isNonNullableByDefault,
|
||||
);
|
||||
|
||||
_addSuggestion(
|
||||
CompletionSuggestion(
|
||||
CompletionSuggestionKind.IDENTIFIER,
|
||||
relevance,
|
||||
name,
|
||||
name.length,
|
||||
0,
|
||||
false,
|
||||
false,
|
||||
returnType: returnType,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Add a suggestion for a static field declared within a class or extension.
|
||||
/// If the field is synthetic, add the corresponding getter instead.
|
||||
///
|
||||
|
|
|
@ -10,6 +10,7 @@ import 'package:analyzer/dart/ast/token.dart';
|
|||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:analyzer_plugin/src/utilities/visitors/local_declaration_visitor.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
/// A contributor that produces suggestions based on the instance members of a
|
||||
/// given type, whether declared by that type directly or inherited from a
|
||||
|
@ -89,6 +90,24 @@ class TypeMemberContributor extends DartCompletionContributor {
|
|||
var memberBuilder = _SuggestionBuilder(request, builder);
|
||||
memberBuilder.buildSuggestions(type,
|
||||
mixins: mixins, superclassConstraints: superclassConstraints);
|
||||
} else if (type is RecordType) {
|
||||
_suggestFromRecordType(type);
|
||||
}
|
||||
}
|
||||
|
||||
void _suggestFromRecordType(RecordType type) {
|
||||
type.positionalFields.forEachIndexed((index, field) {
|
||||
builder.suggestRecordField(
|
||||
field: field,
|
||||
name: '\$$index',
|
||||
);
|
||||
});
|
||||
|
||||
for (final field in type.namedFields) {
|
||||
builder.suggestRecordField(
|
||||
field: field,
|
||||
name: field.name,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -269,6 +269,11 @@ extension CompletionSuggestionExtension
|
|||
element.isNotNull.kind.isParameter;
|
||||
}
|
||||
|
||||
void get isRecordField {
|
||||
kind.isIdentifier;
|
||||
element.isNull;
|
||||
}
|
||||
|
||||
void get isSetter {
|
||||
kind.isIdentifier;
|
||||
element.isNotNull.kind.isSetter;
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
// Copyright (c) 2022, 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:analyzer_utilities/check/check.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import '../../../../client/completion_driver_test.dart';
|
||||
import '../completion_check.dart';
|
||||
|
||||
void main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(RecordTypeTest1);
|
||||
defineReflectiveTests(RecordTypeTest2);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class RecordTypeTest1 extends AbstractCompletionDriverTest
|
||||
with RecordTypeTestCases {
|
||||
@override
|
||||
TestingCompletionProtocol get protocol => TestingCompletionProtocol.version1;
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class RecordTypeTest2 extends AbstractCompletionDriverTest
|
||||
with RecordTypeTestCases {
|
||||
@override
|
||||
TestingCompletionProtocol get protocol => TestingCompletionProtocol.version2;
|
||||
}
|
||||
|
||||
mixin RecordTypeTestCases on AbstractCompletionDriverTest {
|
||||
Future<void> test_mixin() async {
|
||||
var response = await getTestCodeSuggestions('''
|
||||
void f((int, {String foo02}) r) {
|
||||
r.^
|
||||
}
|
||||
''');
|
||||
|
||||
check(response)
|
||||
..hasEmptyReplacement()
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo(r'$0')
|
||||
..isRecordField
|
||||
..returnType.isEqualTo('int'),
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo(r'foo02')
|
||||
..isRecordField
|
||||
..returnType.isEqualTo('String'),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> test_named() async {
|
||||
var response = await getTestCodeSuggestions('''
|
||||
void f(({int foo01, String foo02}) r) {
|
||||
r.^
|
||||
}
|
||||
''');
|
||||
|
||||
check(response)
|
||||
..hasEmptyReplacement()
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo(r'foo01')
|
||||
..isRecordField
|
||||
..returnType.isEqualTo('int'),
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo(r'foo02')
|
||||
..isRecordField
|
||||
..returnType.isEqualTo('String'),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> test_positional() async {
|
||||
var response = await getTestCodeSuggestions('''
|
||||
void f((int, String) r) {
|
||||
r.^
|
||||
}
|
||||
''');
|
||||
|
||||
check(response)
|
||||
..hasEmptyReplacement()
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo(r'$0')
|
||||
..isRecordField
|
||||
..returnType.isEqualTo('int'),
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo(r'$1')
|
||||
..isRecordField
|
||||
..returnType.isEqualTo('String'),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ import 'package:test_reflective_loader/test_reflective_loader.dart';
|
|||
import 'class_test.dart' as class_;
|
||||
import 'enum_test.dart' as enum_;
|
||||
import 'library_test.dart' as library_;
|
||||
import 'record_type_test.dart' as record_type;
|
||||
|
||||
/// Tests suggestions produced for various kinds of declarations.
|
||||
void main() {
|
||||
|
@ -14,5 +15,6 @@ void main() {
|
|||
class_.main();
|
||||
enum_.main();
|
||||
library_.main();
|
||||
record_type.main();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ class InstanceMemberTest2 extends CompletionRelevanceTest
|
|||
}
|
||||
|
||||
mixin InstanceMemberTestCases on CompletionRelevanceTest {
|
||||
Future<void> test_contextType() async {
|
||||
Future<void> test_contextType_interfaceType_method() async {
|
||||
await addTestFile(r'''
|
||||
class A {}
|
||||
class B extends A {}
|
||||
|
@ -56,6 +56,48 @@ void g(E e) {
|
|||
]);
|
||||
}
|
||||
|
||||
Future<void> test_contextType_recordType_named() async {
|
||||
await addTestFile(r'''
|
||||
class A {}
|
||||
class B extends A {}
|
||||
class C extends B {}
|
||||
class D {}
|
||||
|
||||
void f(B _) {}
|
||||
|
||||
void g(({A foo01, B foo02, C foo03, D foo04}) r) {
|
||||
f(r.^);
|
||||
}
|
||||
''');
|
||||
assertOrder([
|
||||
suggestionWith(completion: r'foo02'), // same
|
||||
suggestionWith(completion: r'foo03'), // subtype
|
||||
suggestionWith(completion: r'foo04'), // unrelated
|
||||
suggestionWith(completion: r'foo01'), // supertype
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> test_contextType_recordType_positional() async {
|
||||
await addTestFile(r'''
|
||||
class A {}
|
||||
class B extends A {}
|
||||
class C extends B {}
|
||||
class D {}
|
||||
|
||||
void f(B _) {}
|
||||
|
||||
void g((A, B, C, D) r) {
|
||||
f(r.^);
|
||||
}
|
||||
''');
|
||||
assertOrder([
|
||||
suggestionWith(completion: r'$1'), // same
|
||||
suggestionWith(completion: r'$2'), // subtype
|
||||
suggestionWith(completion: r'$3'), // unrelated
|
||||
suggestionWith(completion: r'$0'), // supertype
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> test_elementKind() async {
|
||||
await addTestFile('''
|
||||
class A {
|
||||
|
|
Loading…
Reference in a new issue