Skip to content

Commit ecf74c5

Browse files
DanTupCommit Queue
authored andcommitted
[analysis_server] Add widget name + documentation to EditableArguments
Change-Id: I8b8424b77ff1f9683934c64636a9f56097be870f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/405123 Reviewed-by: Elliott Brooks <[email protected]> Commit-Queue: Brian Wilkerson <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]>
1 parent 1165528 commit ecf74c5

File tree

7 files changed

+197
-5
lines changed

7 files changed

+197
-5
lines changed

pkg/analysis_server/lib/src/lsp/handlers/custom/editable_arguments/editable_arguments_mixin.dart

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,21 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'package:analysis_server/src/computer/computer_documentation.dart';
56
import 'package:analysis_server/src/utilities/extensions/numeric.dart';
67
import 'package:analyzer/dart/analysis/results.dart';
78
import 'package:analyzer/dart/element/element2.dart';
89
import 'package:analyzer/src/dart/ast/ast.dart';
910
import 'package:analyzer/src/dart/element/element.dart';
11+
import 'package:analyzer/src/dartdoc/dartdoc_directive_info.dart';
1012
import 'package:analyzer/src/utilities/extensions/ast.dart';
1113
import 'package:analyzer/src/utilities/extensions/flutter.dart';
1214

1315
/// Information about the arguments and parameters for an invocation.
1416
typedef EditableInvocationInfo =
1517
({
18+
String? widgetName,
19+
String? widgetDocumentation,
1620
List<FormalParameterElement> parameters,
1721
Map<FormalParameterElement, Expression> parameterArguments,
1822
Map<FormalParameterElement, int> positionalParameterIndexes,
@@ -22,6 +26,8 @@ typedef EditableInvocationInfo =
2226
});
2327

2428
mixin EditableArgumentsMixin {
29+
DartdocDirectiveInfo getDartdocDirectiveInfoFor(ResolvedUnitResult result);
30+
2531
/// Gets the argument list at [offset] that can be edited.
2632
EditableInvocationInfo? getInvocationInfo(
2733
ResolvedUnitResult result,
@@ -37,6 +43,27 @@ mixin EditableArgumentsMixin {
3743
};
3844
});
3945

46+
String? widgetName, widgetDocumentation;
47+
if (invocation is InstanceCreationExpression) {
48+
widgetName = invocation.constructorName.type.name2.lexeme;
49+
50+
if (invocation.constructorName.element case var element?) {
51+
var dartDocInfo = getDartdocDirectiveInfoFor(result);
52+
var dartDocComputer = DartDocumentationComputer(dartDocInfo);
53+
var dartDoc = dartDocComputer.compute(element);
54+
55+
widgetDocumentation = dartDoc?.full;
56+
}
57+
} else if (invocation is InvocationExpression) {
58+
if (invocation.function case Identifier(:var element?)) {
59+
var dartDocInfo = getDartdocDirectiveInfoFor(result);
60+
var dartDocComputer = DartDocumentationComputer(dartDocInfo);
61+
var dartDoc = dartDocComputer.compute(element);
62+
63+
widgetDocumentation = dartDoc?.full;
64+
}
65+
}
66+
4067
// Return the related argument list.
4168
var (parameters, argumentList) = switch (invocation) {
4269
InstanceCreationExpression() => (
@@ -79,6 +106,8 @@ mixin EditableArgumentsMixin {
79106
};
80107

81108
return (
109+
widgetName: widgetName,
110+
widgetDocumentation: widgetDocumentation,
82111
parameters: parameters,
83112
positionalParameterIndexes: positionalParameterIndexes,
84113
parameterArguments: parameterArguments,

pkg/analysis_server/lib/src/lsp/handlers/custom/editable_arguments/handler_edit_argument.dart

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ class EditArgumentHandler extends SharedMessageHandler<EditArgumentParams, Null>
9292
}
9393

9494
var (
95+
:widgetName,
96+
:widgetDocumentation,
9597
:parameters,
9698
:positionalParameterIndexes,
9799
:parameterArguments,
@@ -101,12 +103,14 @@ class EditArgumentHandler extends SharedMessageHandler<EditArgumentParams, Null>
101103
) = invocation;
102104

103105
// Find the parameter we're editing the argument for.
104-
var name = params.edit.name;
105-
var parameter = parameters.firstWhereOrNull((p) => p.name3 == name);
106+
var parameterName = params.edit.name;
107+
var parameter = parameters.firstWhereOrNull(
108+
(p) => p.name3 == parameterName,
109+
);
106110
if (parameter == null) {
107111
return error(
108112
ErrorCodes.RequestFailed,
109-
'Parameter "$name" was not found in ${parameters.map((p) => p.name3).join(', ')}',
113+
'Parameter "$parameterName" was not found in ${parameters.map((p) => p.name3).join(', ')}',
110114
);
111115
}
112116

@@ -127,7 +131,7 @@ class EditArgumentHandler extends SharedMessageHandler<EditArgumentParams, Null>
127131
// known this argument was not editable.
128132
return error(
129133
ErrorCodes.RequestFailed,
130-
"Parameter '$name' is not editable: $notEditableReason",
134+
"Parameter '$parameterName' is not editable: $notEditableReason",
131135
);
132136
}
133137

pkg/analysis_server/lib/src/lsp/handlers/custom/editable_arguments/handler_editable_arguments.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ class EditableArgumentsHandler
9595
var textDocument = server.getVersionedDocumentIdentifier(result.path);
9696

9797
var (
98+
:widgetName,
99+
:widgetDocumentation,
98100
:parameters,
99101
:positionalParameterIndexes,
100102
:parameterArguments,
@@ -126,6 +128,8 @@ class EditableArgumentsHandler
126128

127129
return EditableArguments(
128130
textDocument: textDocument,
131+
name: widgetName,
132+
documentation: widgetDocumentation,
129133
arguments: editableArguments.nonNulls.toList(),
130134
);
131135
}

pkg/analysis_server/lib/src/lsp/handlers/handlers.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import 'package:analysis_server/src/lsp/progress.dart';
1616
import 'package:analysis_server/src/request_handler_mixin.dart';
1717
import 'package:analyzer/dart/analysis/results.dart';
1818
import 'package:analyzer/source/line_info.dart';
19+
import 'package:analyzer/src/dartdoc/dartdoc_directive_info.dart';
1920
import 'package:analyzer/src/util/performance/operation_performance.dart';
2021
import 'package:analyzer/src/utilities/cancellation.dart';
2122
import 'package:analyzer_plugin/protocol/protocol.dart';
@@ -120,6 +121,10 @@ mixin HandlerHelperMixin<S extends AnalysisServer> {
120121
path,
121122
);
122123

124+
DartdocDirectiveInfo getDartdocDirectiveInfoFor(ResolvedUnitResult result) {
125+
return server.getDartdocDirectiveInfoFor(result);
126+
}
127+
123128
ErrorOr<LineInfo> getLineInfo(String path) {
124129
var lineInfo = server.getLineInfo(path);
125130

pkg/analysis_server/test/shared/shared_editable_arguments_tests.dart

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,22 @@ $content
5252
);
5353
}
5454

55+
Matcher hasDocumentation(Matcher matcher) {
56+
return isA<EditableArguments>().having(
57+
(arguments) => arguments.documentation,
58+
'documentation',
59+
matcher,
60+
);
61+
}
62+
63+
Matcher hasName(Matcher matcher) {
64+
return isA<EditableArguments>().having(
65+
(arguments) => arguments.name,
66+
'name',
67+
matcher,
68+
);
69+
}
70+
5571
Matcher isArg(
5672
String name, {
5773
Object? type = anything,
@@ -109,6 +125,67 @@ $content
109125
);
110126
}
111127

128+
test_documentation_literal() async {
129+
var result = await getEditableArgumentsFor('''
130+
class MyWidget extends StatelessWidget {
131+
/// Creates a MyWidget.
132+
const MyWidget(int x);
133+
134+
@override
135+
Widget build(BuildContext context) => MyW^idget(1);
136+
}
137+
''');
138+
expect(result, hasDocumentation(equals('Creates a MyWidget.')));
139+
}
140+
141+
test_documentation_macro() async {
142+
var result = await getEditableArgumentsFor('''
143+
/// {@template my_widget_docs}
144+
/// MyWidget shared docs.
145+
/// {@endtemplate}
146+
class MyWidget extends StatelessWidget {
147+
/// {@macro my_widget_docs}
148+
const MyWidget(int x);
149+
150+
@override
151+
Widget build(BuildContext context) => MyW^idget(1);
152+
}
153+
''');
154+
expect(result, hasDocumentation(equals('MyWidget shared docs.')));
155+
}
156+
157+
test_documentation_missing() async {
158+
var result = await getEditableArgumentsFor('''
159+
class MyWidget extends StatelessWidget {
160+
const MyWidget(int x);
161+
162+
@override
163+
Widget build(BuildContext context) => MyW^idget(1);
164+
}
165+
''');
166+
expect(result, hasDocumentation(isNull));
167+
}
168+
169+
test_documentation_widgetFactory() async {
170+
var result = await getEditableArgumentsFor('''
171+
extension on MyWidget {
172+
/// Creates a Padded.
173+
@widgetFactory
174+
Widget padded(int x) => this;
175+
}
176+
177+
class MyWidget extends StatelessWidget {
178+
const MyWidget(int x);
179+
180+
@override
181+
Widget build(BuildContext context) {
182+
return padd^ed(1);
183+
}
184+
}
185+
''');
186+
expect(result, hasDocumentation(equals('Creates a Padded.')));
187+
}
188+
112189
test_hasArgument() async {
113190
failTestOnErrorDiagnostic = false;
114191
var result = await getEditableArgumentsFor('''
@@ -717,6 +794,49 @@ class MyWidget extends StatelessWidget {
717794
expect(result, hasArgNamed('a1'));
718795
}
719796

797+
test_name_widgetConstructor() async {
798+
var result = await getEditableArgumentsFor('''
799+
class MyWidget extends StatelessWidget {
800+
const MyWidget(int x);
801+
802+
@override
803+
Widget build(BuildContext context) => MyW^idget(1);
804+
}
805+
''');
806+
expect(result, hasName(equals('MyWidget')));
807+
}
808+
809+
test_name_widgetConstructor_named() async {
810+
var result = await getEditableArgumentsFor('''
811+
class MyWidget extends StatelessWidget {
812+
const MyWidget.named(int x);
813+
814+
@override
815+
Widget build(BuildContext context) => MyW^idget.named(1);
816+
}
817+
''');
818+
expect(result, hasName(equals('MyWidget')));
819+
}
820+
821+
test_name_widgetFactory() async {
822+
var result = await getEditableArgumentsFor('''
823+
extension on MyWidget {
824+
@widgetFactory
825+
Widget padded(int x) => this;
826+
}
827+
828+
class MyWidget extends StatelessWidget {
829+
const MyWidget(int x);
830+
831+
@override
832+
Widget build(BuildContext context) {
833+
return padd^ed(1);
834+
}
835+
}
836+
''');
837+
expect(result, hasName(isNull));
838+
}
839+
720840
/// Arguments should be returned in the order of the parameters in the source
721841
/// code. This keeps things consistent across different instances of the same
722842
/// Widget class and prevents the order from changing as a user adds/removes

pkg/analysis_server/tool/lsp_spec/generate_all.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,8 @@ List<LspEntity> getCustomClasses() {
421421
]),
422422
interface('EditableArguments', [
423423
field('textDocument', type: 'TextDocumentIdentifier'),
424+
field('name', type: 'string', canBeUndefined: true),
425+
field('documentation', type: 'string', canBeUndefined: true),
424426
// TODO(dantup): field('refactors', ...),
425427
field('arguments', type: 'EditableArgument', array: true),
426428
]),

third_party/pkg/language_server_protocol/lib/protocol_custom_generated.dart

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1648,15 +1648,21 @@ class EditableArguments implements ToJsonable {
16481648

16491649
final List<EditableArgument> arguments;
16501650

1651-
final TextDocumentIdentifier textDocument;
1651+
final String? documentation;
16521652

1653+
final String? name;
1654+
final TextDocumentIdentifier textDocument;
16531655
EditableArguments({
16541656
required this.arguments,
1657+
this.documentation,
1658+
this.name,
16551659
required this.textDocument,
16561660
});
16571661
@override
16581662
int get hashCode => Object.hash(
16591663
lspHashCode(arguments),
1664+
documentation,
1665+
name,
16601666
textDocument,
16611667
);
16621668

@@ -1665,13 +1671,21 @@ class EditableArguments implements ToJsonable {
16651671
return other is EditableArguments &&
16661672
other.runtimeType == EditableArguments &&
16671673
const DeepCollectionEquality().equals(arguments, other.arguments) &&
1674+
documentation == other.documentation &&
1675+
name == other.name &&
16681676
textDocument == other.textDocument;
16691677
}
16701678

16711679
@override
16721680
Map<String, Object?> toJson() {
16731681
var result = <String, Object?>{};
16741682
result['arguments'] = arguments.map((item) => item.toJson()).toList();
1683+
if (documentation != null) {
1684+
result['documentation'] = documentation;
1685+
}
1686+
if (name != null) {
1687+
result['name'] = name;
1688+
}
16751689
result['textDocument'] = textDocument.toJson();
16761690
return result;
16771691
}
@@ -1685,6 +1699,14 @@ class EditableArguments implements ToJsonable {
16851699
allowsUndefined: false, allowsNull: false)) {
16861700
return false;
16871701
}
1702+
if (!_canParseString(obj, reporter, 'documentation',
1703+
allowsUndefined: true, allowsNull: false)) {
1704+
return false;
1705+
}
1706+
if (!_canParseString(obj, reporter, 'name',
1707+
allowsUndefined: true, allowsNull: false)) {
1708+
return false;
1709+
}
16881710
return _canParseTextDocumentIdentifier(obj, reporter, 'textDocument',
16891711
allowsUndefined: false, allowsNull: false);
16901712
} else {
@@ -1698,11 +1720,17 @@ class EditableArguments implements ToJsonable {
16981720
final arguments = (argumentsJson as List<Object?>)
16991721
.map((item) => EditableArgument.fromJson(item as Map<String, Object?>))
17001722
.toList();
1723+
final documentationJson = json['documentation'];
1724+
final documentation = documentationJson as String?;
1725+
final nameJson = json['name'];
1726+
final name = nameJson as String?;
17011727
final textDocumentJson = json['textDocument'];
17021728
final textDocument = TextDocumentIdentifier.fromJson(
17031729
textDocumentJson as Map<String, Object?>);
17041730
return EditableArguments(
17051731
arguments: arguments,
1732+
documentation: documentation,
1733+
name: name,
17061734
textDocument: textDocument,
17071735
);
17081736
}

0 commit comments

Comments
 (0)