mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 08:44:27 +00:00
[CMSR] Support for formal parameters selection.
Change-Id: I38beddf9eeb36615e11ae1375aac335c1a7a6d56 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/308966 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
parent
a0cf7286af
commit
324cef773c
2 changed files with 297 additions and 31 deletions
|
@ -98,12 +98,16 @@ final class FormalParameterState {
|
|||
/// changing types, so this field should be read-only in UI.
|
||||
final String typeStr;
|
||||
|
||||
/// If `true`, the selection covers this formal parameter.
|
||||
final bool isSelected;
|
||||
|
||||
FormalParameterState({
|
||||
required this.id,
|
||||
required this.kind,
|
||||
required this.positionalIndex,
|
||||
required this.name,
|
||||
required this.typeStr,
|
||||
required this.isSelected,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -209,10 +213,12 @@ final class ValidSelectionState extends SelectionState {
|
|||
class _Declaration {
|
||||
final ExecutableElement element;
|
||||
final AstNode node;
|
||||
final List<FormalParameter> selected;
|
||||
|
||||
_Declaration({
|
||||
required this.element,
|
||||
required this.node,
|
||||
required this.selected,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -286,6 +292,7 @@ class _SelectionAnalyzer {
|
|||
positionalIndex: kind.isPositional ? positionalIndex++ : null,
|
||||
name: nameToken.lexeme,
|
||||
typeStr: refactoringContext.utils.getNodeText(typeNode),
|
||||
isSelected: declaration.selected.contains(parameterNode),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -301,28 +308,19 @@ class _SelectionAnalyzer {
|
|||
final coveringNode = refactoringContext.coveringNode;
|
||||
|
||||
switch (coveringNode) {
|
||||
case FunctionDeclaration():
|
||||
if (refactoringContext.selectionIsInToken(coveringNode.name)) {
|
||||
final element = coveringNode.declaredElement;
|
||||
if (element != null) {
|
||||
return _Declaration(
|
||||
element: element,
|
||||
node: coveringNode,
|
||||
);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
case MethodDeclaration():
|
||||
if (refactoringContext.selectionIsInToken(coveringNode.name)) {
|
||||
final element = coveringNode.declaredElement;
|
||||
if (element != null) {
|
||||
return _Declaration(
|
||||
element: element,
|
||||
node: coveringNode,
|
||||
);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
case FormalParameter():
|
||||
return _declarationFormalParameter(coveringNode);
|
||||
case FormalParameterList():
|
||||
return _declarationFormalParameterList(coveringNode);
|
||||
}
|
||||
|
||||
final atExecutable = _declarationExecutable(
|
||||
node: coveringNode,
|
||||
anyLocation: false,
|
||||
selected: const [],
|
||||
);
|
||||
if (atExecutable != null) {
|
||||
return atExecutable;
|
||||
}
|
||||
|
||||
Element? element;
|
||||
|
@ -348,6 +346,88 @@ class _SelectionAnalyzer {
|
|||
return _Declaration(
|
||||
element: element,
|
||||
node: node,
|
||||
selected: const [],
|
||||
);
|
||||
}
|
||||
|
||||
_Declaration? _declarationExecutable({
|
||||
required AstNode? node,
|
||||
required bool anyLocation,
|
||||
required List<FormalParameter> selected,
|
||||
}) {
|
||||
bool hasGoodLocation(Token? name) {
|
||||
return anyLocation || refactoringContext.selectionIsInToken(name);
|
||||
}
|
||||
|
||||
_Declaration? buildDeclaration(Declaration node) {
|
||||
final element = node.declaredElement;
|
||||
if (element is ExecutableElement) {
|
||||
return _Declaration(
|
||||
element: element,
|
||||
node: node,
|
||||
selected: selected,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (node is FunctionExpression) {
|
||||
final functionDeclaration = node.parent;
|
||||
if (functionDeclaration is FunctionDeclaration) {
|
||||
node = functionDeclaration;
|
||||
}
|
||||
}
|
||||
|
||||
switch (node) {
|
||||
case FunctionDeclaration():
|
||||
if (hasGoodLocation(node.name)) {
|
||||
return buildDeclaration(node);
|
||||
}
|
||||
case MethodDeclaration():
|
||||
if (hasGoodLocation(node.name)) {
|
||||
return buildDeclaration(node);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
_Declaration? _declarationFormalParameter(FormalParameter node) {
|
||||
final FormalParameter formalParameter;
|
||||
if (node.parent case DefaultFormalParameter result) {
|
||||
formalParameter = result;
|
||||
} else {
|
||||
formalParameter = node;
|
||||
}
|
||||
|
||||
final formalParameterList = formalParameter.parent;
|
||||
if (formalParameterList is! FormalParameterList) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return _declarationExecutable(
|
||||
node: formalParameterList.parent,
|
||||
anyLocation: true,
|
||||
selected: [formalParameter],
|
||||
);
|
||||
}
|
||||
|
||||
_Declaration? _declarationFormalParameterList(FormalParameterList node) {
|
||||
final selection = refactoringContext.selection;
|
||||
final selectedNodes = selection?.nodesInRange();
|
||||
if (selectedNodes == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final selected = selectedNodes.whereType<FormalParameter>().toList();
|
||||
if (selected.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return _declarationExecutable(
|
||||
node: node.parent,
|
||||
anyLocation: true,
|
||||
selected: selected,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import 'package:analysis_server/src/services/search/search_engine_internal.dart'
|
|||
import 'package:analyzer/dart/analysis/results.dart';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/file_system/file_system.dart';
|
||||
import 'package:analyzer/source/source_range.dart';
|
||||
import 'package:analyzer/src/dart/element/element.dart';
|
||||
import 'package:analyzer/src/summary2/reference.dart';
|
||||
import 'package:analyzer/src/test_utilities/test_code_format.dart';
|
||||
|
@ -69,9 +70,16 @@ class AbstractChangeMethodSignatureTest extends AbstractContextTest {
|
|||
required TestCode testCode,
|
||||
}) async {
|
||||
// There must be exactly one position.
|
||||
expect(testCode.ranges, isEmpty);
|
||||
expect(testCode.positions, hasLength(1));
|
||||
final position = testCode.position;
|
||||
final singlePosition = testCode.positions.singleOrNull;
|
||||
final singleRange = testCode.ranges.singleOrNull;
|
||||
final SourceRange selectionRange;
|
||||
if (singlePosition != null && singleRange == null) {
|
||||
selectionRange = SourceRange(singlePosition.offset, 0);
|
||||
} else if (singlePosition == null && singleRange != null) {
|
||||
selectionRange = singleRange.sourceRange;
|
||||
} else {
|
||||
fail('Expected exactly one: $singlePosition $singleRange');
|
||||
}
|
||||
|
||||
final analysisSession = await session;
|
||||
|
||||
|
@ -86,8 +94,8 @@ class AbstractChangeMethodSignatureTest extends AbstractContextTest {
|
|||
searchEngine: SearchEngineImpl(allDrivers),
|
||||
resolvedLibraryResult: resolvedLibraryResult,
|
||||
resolvedUnitResult: unitResult,
|
||||
selectionOffset: position.offset,
|
||||
selectionLength: 0,
|
||||
selectionOffset: selectionRange.offset,
|
||||
selectionLength: selectionRange.length,
|
||||
includeExperimental: true,
|
||||
);
|
||||
}
|
||||
|
@ -153,7 +161,182 @@ formalParameters
|
|||
''');
|
||||
}
|
||||
|
||||
Future<void> test_formalParameters_optionalNamed() async {
|
||||
Future<void> test_formalParameters_requiredNamed_full() async {
|
||||
await _analyzeSelection(r'''
|
||||
void test({
|
||||
[!required int a!],
|
||||
required int b,
|
||||
}) {}
|
||||
''');
|
||||
|
||||
_assertSelectionState(selectionState, r'''
|
||||
element: self::@function::test
|
||||
formalParameters
|
||||
id: 0
|
||||
kind: requiredNamed
|
||||
name: a
|
||||
typeStr: int
|
||||
selected
|
||||
id: 1
|
||||
kind: requiredNamed
|
||||
name: b
|
||||
typeStr: int
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_formalParameters_requiredNamed_multiple() async {
|
||||
await _analyzeSelection(r'''
|
||||
void test({
|
||||
required int a,
|
||||
[!required int b,
|
||||
required int c,!]
|
||||
required int d,
|
||||
}) {}
|
||||
''');
|
||||
|
||||
_assertSelectionState(selectionState, r'''
|
||||
element: self::@function::test
|
||||
formalParameters
|
||||
id: 0
|
||||
kind: requiredNamed
|
||||
name: a
|
||||
typeStr: int
|
||||
id: 1
|
||||
kind: requiredNamed
|
||||
name: b
|
||||
typeStr: int
|
||||
selected
|
||||
id: 2
|
||||
kind: requiredNamed
|
||||
name: c
|
||||
typeStr: int
|
||||
selected
|
||||
id: 3
|
||||
kind: requiredNamed
|
||||
name: d
|
||||
typeStr: int
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_formalParameters_requiredNamed_name_full() async {
|
||||
await _analyzeSelection(r'''
|
||||
void test({
|
||||
required int [!aaaa!],
|
||||
required int bbbb,
|
||||
}) {}
|
||||
''');
|
||||
|
||||
_assertSelectionState(selectionState, r'''
|
||||
element: self::@function::test
|
||||
formalParameters
|
||||
id: 0
|
||||
kind: requiredNamed
|
||||
name: aaaa
|
||||
typeStr: int
|
||||
selected
|
||||
id: 1
|
||||
kind: requiredNamed
|
||||
name: bbbb
|
||||
typeStr: int
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_formalParameters_requiredNamed_name_partial() async {
|
||||
await _analyzeSelection(r'''
|
||||
void test({
|
||||
required int a[!aa!]a,
|
||||
required int bbbb,
|
||||
}) {}
|
||||
''');
|
||||
|
||||
_assertSelectionState(selectionState, r'''
|
||||
element: self::@function::test
|
||||
formalParameters
|
||||
id: 0
|
||||
kind: requiredNamed
|
||||
name: aaaa
|
||||
typeStr: int
|
||||
selected
|
||||
id: 1
|
||||
kind: requiredNamed
|
||||
name: bbbb
|
||||
typeStr: int
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_formalParameters_requiredNamed_name_position() async {
|
||||
await _analyzeSelection(r'''
|
||||
void test({
|
||||
required int ^a,
|
||||
required int b
|
||||
}) {}
|
||||
''');
|
||||
|
||||
_assertSelectionState(selectionState, r'''
|
||||
element: self::@function::test
|
||||
formalParameters
|
||||
id: 0
|
||||
kind: requiredNamed
|
||||
name: a
|
||||
typeStr: int
|
||||
selected
|
||||
id: 1
|
||||
kind: requiredNamed
|
||||
name: b
|
||||
typeStr: int
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_formalParameters_requiredPositional_multiple() async {
|
||||
await _analyzeSelection(r'''
|
||||
void test(int a, [!int b, int c,!] int d) {}
|
||||
''');
|
||||
|
||||
_assertSelectionState(selectionState, r'''
|
||||
element: self::@function::test
|
||||
formalParameters
|
||||
id: 0
|
||||
kind: requiredPositional
|
||||
name: a
|
||||
typeStr: int
|
||||
id: 1
|
||||
kind: requiredPositional
|
||||
name: b
|
||||
typeStr: int
|
||||
selected
|
||||
id: 2
|
||||
kind: requiredPositional
|
||||
name: c
|
||||
typeStr: int
|
||||
selected
|
||||
id: 3
|
||||
kind: requiredPositional
|
||||
name: d
|
||||
typeStr: int
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_formalParameters_requiredPositional_name() async {
|
||||
await _analyzeSelection(r'''
|
||||
void test(int ^a, int b) {}
|
||||
''');
|
||||
|
||||
_assertSelectionState(selectionState, r'''
|
||||
element: self::@function::test
|
||||
formalParameters
|
||||
id: 0
|
||||
kind: requiredPositional
|
||||
name: a
|
||||
typeStr: int
|
||||
selected
|
||||
id: 1
|
||||
kind: requiredPositional
|
||||
name: b
|
||||
typeStr: int
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_kind_optionalNamed() async {
|
||||
await _analyzeSelection(r'''
|
||||
void ^test({int? a}) {}
|
||||
''');
|
||||
|
@ -168,7 +351,7 @@ formalParameters
|
|||
''');
|
||||
}
|
||||
|
||||
Future<void> test_formalParameters_optionalPositional() async {
|
||||
Future<void> test_kind_optionalPositional() async {
|
||||
await _analyzeSelection(r'''
|
||||
void ^test([int? a]) {}
|
||||
''');
|
||||
|
@ -183,7 +366,7 @@ formalParameters
|
|||
''');
|
||||
}
|
||||
|
||||
Future<void> test_formalParameters_requiredNamed() async {
|
||||
Future<void> test_kind_requiredNamed() async {
|
||||
await _analyzeSelection(r'''
|
||||
void ^test({required int a}) {}
|
||||
''');
|
||||
|
@ -198,7 +381,7 @@ formalParameters
|
|||
''');
|
||||
}
|
||||
|
||||
Future<void> test_formalParameters_requiredPositional2() async {
|
||||
Future<void> test_kind_requiredPositional2() async {
|
||||
await _analyzeSelection(r'''
|
||||
void ^test(int a, String b) {}
|
||||
''');
|
||||
|
@ -287,6 +470,9 @@ NoExecutableElementSelectionState
|
|||
buffer.writeln(' kind: ${formalParameter.kind.name}');
|
||||
buffer.writeln(' name: ${formalParameter.name}');
|
||||
buffer.writeln(' typeStr: ${formalParameter.typeStr}');
|
||||
if (formalParameter.isSelected) {
|
||||
buffer.writeln(' selected');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue