Skip to content

Commit c24f36d

Browse files
DanTupCommit Queue
authored and
Commit Queue
committed
[analysis_server] Use public (non-src) URIs for Flutter snippet imports
Fixes #49081. Change-Id: I0734b4f45c72d70f7b32640bed6b6ec2e8130c01 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/273841 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Brian Wilkerson <[email protected]>
1 parent badf1c5 commit c24f36d

17 files changed

+243
-154
lines changed

pkg/analysis_server/lib/src/cider/fixes.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import 'package:analysis_server/plugin/edit/fix/fix_core.dart';
66
import 'package:analysis_server/src/services/correction/change_workspace.dart';
77
import 'package:analysis_server/src/services/correction/fix.dart';
8-
import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart';
98
import 'package:analysis_server/src/services/correction/fix_internal.dart';
109
import 'package:analyzer/dart/analysis/results.dart';
1110
import 'package:analyzer/dart/element/element.dart';
@@ -15,6 +14,7 @@ import 'package:analyzer/source/line_info.dart';
1514
import 'package:analyzer/src/dart/analysis/file_state.dart';
1615
import 'package:analyzer/src/dart/analysis/performance_logger.dart';
1716
import 'package:analyzer/src/dart/micro/resolve_file.dart';
17+
import 'package:analyzer/src/services/top_level_declarations.dart';
1818
import 'package:analyzer_plugin/utilities/change_builder/change_workspace.dart';
1919

2020
class CiderErrorFixes {

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,9 @@ class CompletionHandler extends MessageHandler<CompletionParams, CompletionList>
519519
try {
520520
unrankedResults =
521521
await performance.runAsync('getSnippets', (performance) async {
522+
// TODO(dantup): Pass `fuzzy` into here so we can filter snippets
523+
// before computing them to avoid looking up Element->Public Library
524+
// if they won't be included.
522525
final snippets = await _getDartSnippetItems(
523526
clientCapabilities: capabilities,
524527
unit: unit,

pkg/analysis_server/lib/src/services/correction/fix.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44

55
import 'package:analysis_server/plugin/edit/fix/fix_dart.dart';
66
import 'package:analysis_server/src/services/correction/fix/dart/extensions.dart';
7-
import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart';
87
import 'package:analysis_server/src/services/correction/fix_internal.dart';
98
import 'package:analyzer/dart/analysis/results.dart';
109
import 'package:analyzer/dart/element/element.dart';
1110
import 'package:analyzer/error/error.dart';
1211
import 'package:analyzer/instrumentation/service.dart';
1312
import 'package:analyzer/src/error/codes.dart';
13+
import 'package:analyzer/src/services/top_level_declarations.dart';
1414
import 'package:analyzer_plugin/utilities/change_builder/change_workspace.dart';
1515
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
1616

pkg/analysis_server/lib/src/services/snippets/dart/flutter_stateful_widget.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ class FlutterStatefulWidget extends FlutterSnippetProducer
3131
final classStatefulWidget = this.classStatefulWidget!;
3232
final classState = this.classState!;
3333

34-
await builder.addDartFileEdit(request.filePath, (builder) {
34+
await builder.addDartFileEdit(request.filePath, (builder) async {
35+
await addImports(builder);
36+
3537
builder.addReplacement(request.replacementRange, (builder) {
3638
// Write the StatefulWidget class
3739
builder.writeClassDeclaration(

pkg/analysis_server/lib/src/services/snippets/dart/flutter_stateful_widget_with_animation.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ class FlutterStatefulWidgetWithAnimationController
3737
final classSingleTickerProviderStateMixin =
3838
this.classSingleTickerProviderStateMixin!;
3939

40-
await builder.addDartFileEdit(request.filePath, (builder) {
40+
await builder.addDartFileEdit(request.filePath, (builder) async {
41+
await addImports(builder);
42+
4143
builder.addReplacement(request.replacementRange, (builder) {
4244
// Write the StatefulWidget class
4345
builder.writeClassDeclaration(

pkg/analysis_server/lib/src/services/snippets/dart/flutter_stateless_widget.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ class FlutterStatelessWidget extends FlutterSnippetProducer
2828
// Checked by isValid().
2929
final classStatelessWidget = this.classStatelessWidget!;
3030

31-
await builder.addDartFileEdit(request.filePath, (builder) {
31+
await builder.addDartFileEdit(request.filePath, (builder) async {
32+
await addImports(builder);
33+
3234
builder.addReplacement(request.replacementRange, (builder) {
3335
builder.writeClassDeclaration(
3436
widgetClassName,

pkg/analysis_server/lib/src/services/snippets/snippet_producer.dart

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import 'package:analyzer/dart/element/nullability_suffix.dart';
1313
import 'package:analyzer/dart/element/type.dart';
1414
import 'package:analyzer/src/dart/analysis/session_helper.dart';
1515
import 'package:analyzer/src/lint/linter.dart';
16+
import 'package:analyzer_plugin/src/utilities/change_builder/change_builder_dart.dart'
17+
show DartFileEditBuilderImpl;
1618
import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
1719
import 'package:meta/meta.dart';
1820

@@ -50,13 +52,38 @@ abstract class FlutterSnippetProducer extends DartSnippetProducer {
5052
late ClassElement? classWidget;
5153
late ClassElement? classPlaceholder;
5254

55+
/// Elements that need to be imported for generated code to be valid.
56+
///
57+
/// Calling [getClass] or [getMixin] records elements in this set.
58+
/// Calling [addImports] will add any required imports to the supplied
59+
/// builder.
60+
final Set<Element> _requiredElementImports = {};
61+
5362
FlutterSnippetProducer(super.request);
5463

55-
Future<ClassElement?> getClass(String name) =>
56-
sessionHelper.getClass(flutter.widgetsUri, name);
64+
/// Adds public imports for any elements fetched by [getClass] and [getMixin]
65+
/// to [builder].
66+
Future<void> addImports(DartFileEditBuilder builder) async {
67+
final dartBuilder = builder as DartFileEditBuilderImpl;
68+
await Future.wait(
69+
_requiredElementImports.map(dartBuilder.importElementLibrary));
70+
}
5771

58-
Future<MixinElement?> getMixin(String name) =>
59-
sessionHelper.getMixin(flutter.widgetsUri, name);
72+
Future<ClassElement?> getClass(String name) async {
73+
final class_ = await sessionHelper.getClass(flutter.widgetsUri, name);
74+
if (class_ != null) {
75+
_requiredElementImports.add(class_);
76+
}
77+
return class_;
78+
}
79+
80+
Future<MixinElement?> getMixin(String name) async {
81+
final mixin = await sessionHelper.getMixin(flutter.widgetsUri, name);
82+
if (mixin != null) {
83+
_requiredElementImports.add(mixin);
84+
}
85+
return mixin;
86+
}
6087

6188
DartType getType(
6289
InterfaceElement classElement, [

pkg/analysis_server/test/lsp/completion_dart_test.dart

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3731,8 +3731,7 @@ void f() {
37313731
class FlutterSnippetCompletionTest extends SnippetCompletionTest {
37323732
/// Standard import statements expected for basic Widgets.
37333733
String get expectedImports => '''
3734-
import 'package:flutter/src/widgets/framework.dart';
3735-
import 'package:flutter/src/widgets/placeholder.dart';''';
3734+
import 'package:flutter/widgets.dart';''';
37363735

37373736
/// Nullability suffix expected in this test class.
37383737
///
@@ -4007,9 +4006,7 @@ class FlutterSnippetCompletionWithoutNullSafetyTest
40074006
extends FlutterSnippetCompletionTest {
40084007
@override
40094008
String get expectedImports => '''
4010-
import 'package:flutter/src/foundation/key.dart';
4011-
import 'package:flutter/src/widgets/framework.dart';
4012-
import 'package:flutter/src/widgets/placeholder.dart';''';
4009+
import 'package:flutter/widgets.dart';''';
40134010

40144011
@override
40154012
String get expectedNullableSuffix => '';

pkg/analysis_server/test/services/snippets/dart/flutter_stateful_widget_test.dart

Lines changed: 10 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'package:analysis_server/src/protocol_server.dart';
66
import 'package:analysis_server/src/services/snippets/dart/flutter_stateful_widget.dart';
7+
import 'package:analyzer/src/test_utilities/test_code_format.dart';
78
import 'package:test/test.dart';
89
import 'package:test_reflective_loader/test_reflective_loader.dart';
910

@@ -38,9 +39,7 @@ class FlutterStatefulWidgetTest extends FlutterSnippetProducerTest {
3839
code = SourceEdit.applySequence(code, edit.edits);
3940
}
4041
expect(code, '''
41-
import 'package:flutter/src/foundation/key.dart';
42-
import 'package:flutter/src/widgets/framework.dart';
43-
import 'package:flutter/src/widgets/placeholder.dart';
42+
import 'package:flutter/widgets.dart';
4443
4544
class MyWidget extends StatefulWidget {
4645
const MyWidget({Key? key}) : super(key: key);
@@ -69,44 +68,22 @@ class _MyWidgetState extends State<MyWidget> {
6968
final snippet = await expectValidSnippet('^');
7069
expect(snippet.prefix, prefix);
7170
expect(snippet.label, label);
72-
var code = '';
73-
expect(snippet.change.edits, hasLength(1));
74-
for (var edit in snippet.change.edits) {
75-
code = SourceEdit.applySequence(code, edit.edits);
76-
}
77-
expect(code, '''
78-
import 'package:flutter/src/widgets/framework.dart';
79-
import 'package:flutter/src/widgets/placeholder.dart';
71+
final expected = TestCode.parse('''
72+
import 'package:flutter/widgets.dart';
8073
81-
class MyWidget extends StatefulWidget {
82-
const MyWidget({super.key});
74+
class /*0*/MyWidget extends StatefulWidget {
75+
const /*1*/MyWidget({super.key});
8376
8477
@override
85-
State<MyWidget> createState() => _MyWidgetState();
78+
State</*2*/MyWidget> createState() => _/*3*/MyWidgetState();
8679
}
8780
88-
class _MyWidgetState extends State<MyWidget> {
81+
class _/*4*/MyWidgetState extends State</*5*/MyWidget> {
8982
@override
9083
Widget build(BuildContext context) {
91-
return const Placeholder();
84+
return /*[0*/const Placeholder()/*0]*/;
9285
}
9386
}''');
94-
expect(snippet.change.selection!.file, testFile);
95-
expect(snippet.change.selection!.offset, 358);
96-
expect(snippet.change.selectionLength, 19);
97-
expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
98-
{
99-
'positions': [
100-
{'file': testFile, 'offset': 115},
101-
{'file': testFile, 'offset': 157},
102-
{'file': testFile, 'offset': 201},
103-
{'file': testFile, 'offset': 229},
104-
{'file': testFile, 'offset': 256},
105-
{'file': testFile, 'offset': 284},
106-
],
107-
'length': 8,
108-
'suggestions': []
109-
}
110-
]);
87+
assertFlutterSnippetChange(snippet.change, 'MyWidget', expected);
11188
}
11289
}

pkg/analysis_server/test/services/snippets/dart/flutter_stateful_widget_with_animation_controller_test.dart

Lines changed: 10 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'package:analysis_server/src/protocol_server.dart';
66
import 'package:analysis_server/src/services/snippets/dart/flutter_stateful_widget_with_animation.dart';
7+
import 'package:analyzer/src/test_utilities/test_code_format.dart';
78
import 'package:test/test.dart';
89
import 'package:test_reflective_loader/test_reflective_loader.dart';
910

@@ -39,11 +40,7 @@ class FlutterStatefulWidgetWithAnimationControllerTest
3940
code = SourceEdit.applySequence(code, edit.edits);
4041
}
4142
expect(code, '''
42-
import 'package:flutter/src/animation/animation_controller.dart';
43-
import 'package:flutter/src/foundation/key.dart';
44-
import 'package:flutter/src/widgets/framework.dart';
45-
import 'package:flutter/src/widgets/placeholder.dart';
46-
import 'package:flutter/src/widgets/ticker_provider.dart';
43+
import 'package:flutter/widgets.dart';
4744
4845
class MyWidget extends StatefulWidget {
4946
const MyWidget({Key? key}) : super(key: key);
@@ -87,25 +84,17 @@ class _MyWidgetState extends State<MyWidget>
8784
final snippet = await expectValidSnippet('^');
8885
expect(snippet.prefix, prefix);
8986
expect(snippet.label, label);
90-
var code = '';
91-
expect(snippet.change.edits, hasLength(1));
92-
for (var edit in snippet.change.edits) {
93-
code = SourceEdit.applySequence(code, edit.edits);
94-
}
95-
expect(code, '''
96-
import 'package:flutter/src/animation/animation_controller.dart';
97-
import 'package:flutter/src/widgets/framework.dart';
98-
import 'package:flutter/src/widgets/placeholder.dart';
99-
import 'package:flutter/src/widgets/ticker_provider.dart';
87+
final expected = TestCode.parse('''
88+
import 'package:flutter/widgets.dart';
10089
101-
class MyWidget extends StatefulWidget {
102-
const MyWidget({super.key});
90+
class /*0*/MyWidget extends StatefulWidget {
91+
const /*1*/MyWidget({super.key});
10392
10493
@override
105-
State<MyWidget> createState() => _MyWidgetState();
94+
State</*2*/MyWidget> createState() => _/*3*/MyWidgetState();
10695
}
10796
108-
class _MyWidgetState extends State<MyWidget>
97+
class _/*4*/MyWidgetState extends State</*5*/MyWidget>
10998
with SingleTickerProviderStateMixin {
11099
late AnimationController _controller;
111100
@@ -123,25 +112,9 @@ class _MyWidgetState extends State<MyWidget>
123112
124113
@override
125114
Widget build(BuildContext context) {
126-
return const Placeholder();
115+
return /*[0*/const Placeholder()/*0]*/;
127116
}
128117
}''');
129-
expect(snippet.change.selection!.file, testFile);
130-
expect(snippet.change.selection!.offset, 761);
131-
expect(snippet.change.selectionLength, 19);
132-
expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
133-
{
134-
'positions': [
135-
{'file': testFile, 'offset': 240},
136-
{'file': testFile, 'offset': 282},
137-
{'file': testFile, 'offset': 326},
138-
{'file': testFile, 'offset': 354},
139-
{'file': testFile, 'offset': 381},
140-
{'file': testFile, 'offset': 409},
141-
],
142-
'length': 8,
143-
'suggestions': []
144-
}
145-
]);
118+
assertFlutterSnippetChange(snippet.change, 'MyWidget', expected);
146119
}
147120
}

pkg/analysis_server/test/services/snippets/dart/flutter_stateless_widget_test.dart

Lines changed: 15 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
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/protocol_server.dart';
65
import 'package:analysis_server/src/services/snippets/dart/flutter_stateless_widget.dart';
6+
import 'package:analyzer/src/test_utilities/test_code_format.dart';
77
import 'package:test/test.dart';
88
import 'package:test_reflective_loader/test_reflective_loader.dart';
99

@@ -32,24 +32,18 @@ class FlutterStatelessWidgetTest extends FlutterSnippetProducerTest {
3232
final snippet = await expectValidSnippet('^');
3333
expect(snippet.prefix, prefix);
3434
expect(snippet.label, label);
35-
var code = '';
36-
expect(snippet.change.edits, hasLength(1));
37-
for (var edit in snippet.change.edits) {
38-
code = SourceEdit.applySequence(code, edit.edits);
39-
}
40-
expect(code, '''
41-
import 'package:flutter/src/foundation/key.dart';
42-
import 'package:flutter/src/widgets/framework.dart';
43-
import 'package:flutter/src/widgets/placeholder.dart';
44-
45-
class MyWidget extends StatelessWidget {
46-
const MyWidget({Key? key}) : super(key: key);
35+
final expected = TestCode.parse('''
36+
import 'package:flutter/widgets.dart';
37+
38+
class /*0*/MyWidget extends StatelessWidget {
39+
const /*1*/MyWidget({Key? key}) : super(key: key);
4740
4841
@override
4942
Widget build(BuildContext context) {
50-
return const Placeholder();
43+
return /*[0*/const Placeholder()/*0]*/;
5144
}
5245
}''');
46+
assertFlutterSnippetChange(snippet.change, 'MyWidget', expected);
5347
}
5448

5549
Future<void> test_notValid_notFlutterProject() async {
@@ -64,35 +58,17 @@ class MyWidget extends StatelessWidget {
6458
final snippet = await expectValidSnippet('^');
6559
expect(snippet.prefix, prefix);
6660
expect(snippet.label, label);
67-
var code = '';
68-
expect(snippet.change.edits, hasLength(1));
69-
for (var edit in snippet.change.edits) {
70-
code = SourceEdit.applySequence(code, edit.edits);
71-
}
72-
expect(code, '''
73-
import 'package:flutter/src/widgets/framework.dart';
74-
import 'package:flutter/src/widgets/placeholder.dart';
75-
76-
class MyWidget extends StatelessWidget {
77-
const MyWidget({super.key});
61+
final expected = TestCode.parse('''
62+
import 'package:flutter/widgets.dart';
63+
64+
class /*0*/MyWidget extends StatelessWidget {
65+
const /*1*/MyWidget({super.key});
7866
7967
@override
8068
Widget build(BuildContext context) {
81-
return const Placeholder();
69+
return /*[0*/const Placeholder()/*0]*/;
8270
}
8371
}''');
84-
expect(snippet.change.selection!.file, testFile);
85-
expect(snippet.change.selection!.offset, 244);
86-
expect(snippet.change.selectionLength, 19);
87-
expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
88-
{
89-
'positions': [
90-
{'file': testFile, 'offset': 115},
91-
{'file': testFile, 'offset': 158},
92-
],
93-
'length': 8,
94-
'suggestions': []
95-
}
96-
]);
72+
assertFlutterSnippetChange(snippet.change, 'MyWidget', expected);
9773
}
9874
}

0 commit comments

Comments
 (0)