Attention is currently required from: Konstantin Shcheglov.
Brian Wilkerson would like Konstantin Shcheglov to review this change.
Add completion support for map patterns
Change-Id: I8560ecdabb484290066b6cc4cb2a77f72163a995
---
M pkg/analysis_server/lib/src/services/completion/dart/keyword_contributor.dart
A pkg/analysis_server/test/services/completion/dart/location/map_pattern_test.dart
M pkg/analysis_server/test/services/completion/dart/location/test_all.dart
M pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
4 files changed, 252 insertions(+), 0 deletions(-)
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/keyword_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/keyword_contributor.dart
index 736647b..04a9259 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/keyword_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/keyword_contributor.dart
@@ -630,6 +630,19 @@
}
@override
+ void visitMapPattern(MapPattern node) {
+ _addConstantExpressionKeywords(node);
+ super.visitMapPattern(node);
+ }
+
+ @override
+ void visitMapPatternEntry(MapPatternEntry node) {
+ _addSuggestions([Keyword.FINAL, Keyword.VAR]);
+ _addExpressionKeywords(node);
+ super.visitMapPatternEntry(node);
+ }
+
+ @override
void visitMethodDeclaration(MethodDeclaration node) {
if (entity == node.body) {
if (node.body.isEmpty) {
diff --git a/pkg/analysis_server/test/services/completion/dart/location/map_pattern_test.dart b/pkg/analysis_server/test/services/completion/dart/location/map_pattern_test.dart
new file mode 100644
index 0000000..0ceba55
--- /dev/null
+++ b/pkg/analysis_server/test/services/completion/dart/location/map_pattern_test.dart
@@ -0,0 +1,211 @@
+// Copyright (c) 2023, 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:test_reflective_loader/test_reflective_loader.dart';
+
+import '../../../../client/completion_driver_test.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(MapPatternTest1);
+ defineReflectiveTests(MapPatternTest2);
+ });
+}
+
+@reflectiveTest
+class MapPatternTest1 extends AbstractCompletionDriverTest
+ with MapPatternTestCases {
+ @override
+ TestingCompletionProtocol get protocol => TestingCompletionProtocol.version1;
+}
+
+@reflectiveTest
+class MapPatternTest2 extends AbstractCompletionDriverTest
+ with MapPatternTestCases {
+ @override
+ TestingCompletionProtocol get protocol => TestingCompletionProtocol.version2;
+}
+
+mixin MapPatternTestCases on AbstractCompletionDriverTest {
+ Future<void> test_entry_key_first() async {
+ await computeSuggestions('''
+const c01 = '1';
+var v01 = '2';
+void f(Object o1) {
+ const c11 = '3';
+ var v11 = '4';
+ switch (o1) {
+ case <String, int>{^ c01 : 1, c11 : 3]:
+ }
+}
+''');
+ assertResponse('''
+suggestions
+ c01
+ kind: topLevelVariable
+ c11
+ kind: localVariable
+ v01
+ kind: topLevelVariable
+ v11
+ kind: localVariable
+ false
+ kind: keyword
+ null
+ kind: keyword
+ true
+ kind: keyword
+ o1
+ kind: parameter
+ const
+ kind: keyword
+''');
+ }
+
+ Future<void> test_entry_key_last() async {
+ await computeSuggestions('''
+const c01 = '1';
+var v01 = '2';
+void f(Object o1) {
+ const c11 = '3';
+ var v11 = '4';
+ switch (o1) {
+ case <String, int>{c01 : 1, c11 : 3, ^}:
+ }
+}
+''');
+ assertResponse('''
+suggestions
+ c01
+ kind: topLevelVariable
+ c11
+ kind: localVariable
+ v01
+ kind: topLevelVariable
+ v11
+ kind: localVariable
+ false
+ kind: keyword
+ null
+ kind: keyword
+ true
+ kind: keyword
+ o1
+ kind: parameter
+ const
+ kind: keyword
+''');
+ }
+
+ Future<void> test_entry_key_middle() async {
+ await computeSuggestions('''
+const c01 = '1';
+var v01 = '2';
+void f(Object o1) {
+ const c11 = '3';
+ var v11 = '4';
+ switch (o1) {
+ case <String, int>{c01 : 1, ^, c11 : 3}:
+ }
+}
+''');
+ assertResponse('''
+suggestions
+ c01
+ kind: topLevelVariable
+ c11
+ kind: localVariable
+ v01
+ kind: topLevelVariable
+ v11
+ kind: localVariable
+ false
+ kind: keyword
+ null
+ kind: keyword
+ true
+ kind: keyword
+ o1
+ kind: parameter
+ const
+ kind: keyword
+''');
+ }
+
+ Future<void> test_entry_key_only() async {
+ await computeSuggestions('''
+const c01 = '1';
+var v01 = '2';
+void f(Object o1) {
+ const c11 = '3';
+ var v11 = '4';
+ switch (o1) {
+ case <String, int>{^}:
+ }
+}
+''');
+ assertResponse('''
+suggestions
+ c01
+ kind: topLevelVariable
+ c11
+ kind: localVariable
+ v01
+ kind: topLevelVariable
+ v11
+ kind: localVariable
+ false
+ kind: keyword
+ null
+ kind: keyword
+ true
+ kind: keyword
+ o1
+ kind: parameter
+ const
+ kind: keyword
+''');
+ }
+
+ Future<void> test_entry_value() async {
+ await computeSuggestions('''
+const c01 = '1';
+var v01 = '2';
+void f(Object o1) {
+ const c11 = '3';
+ var v11 = '4';
+ switch (o1) {
+ case <String, int>{c01 : ^}:
+ }
+}
+''');
+ assertResponse('''
+suggestions
+ c01
+ kind: topLevelVariable
+ c11
+ kind: localVariable
+ false
+ kind: keyword
+ null
+ kind: keyword
+ true
+ kind: keyword
+ v01
+ kind: topLevelVariable
+ v11
+ kind: localVariable
+ o1
+ kind: parameter
+ const
+ kind: keyword
+ final
+ kind: keyword
+ switch
+ kind: keyword
+ var
+ kind: keyword
+''');
+ }
+}
diff --git a/pkg/analysis_server/test/services/completion/dart/location/test_all.dart b/pkg/analysis_server/test/services/completion/dart/location/test_all.dart
index cb3ec5b..9cf3b38 100644
--- a/pkg/analysis_server/test/services/completion/dart/location/test_all.dart
+++ b/pkg/analysis_server/test/services/completion/dart/location/test_all.dart
@@ -16,6 +16,7 @@
import 'if_element_test.dart' as if_element;
import 'if_statement_test.dart' as if_statement;
import 'list_pattern_test.dart' as list_pattern;
+import 'map_pattern_test.dart' as map_pattern;
import 'named_expression_test.dart' as named_expression;
import 'object_pattern_test.dart' as object_pattern;
import 'record_literal_test.dart' as record_literal;
@@ -40,6 +41,7 @@
if_element.main();
if_statement.main();
list_pattern.main();
+ map_pattern.main();
named_expression.main();
object_pattern.main();
record_literal.main();
diff --git a/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart b/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
index fc6faa0..4448c7f 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
@@ -970,6 +970,23 @@
}
@override
+ void visitMapPattern(MapPattern node) {
+ optype.completionLocation = 'MapPattern_entry';
+ optype.includeReturnValueSuggestions = true;
+ optype.includeTypeNameSuggestions = true;
+ optype.includeVarNameSuggestions = true;
+ optype.mustBeConst = true;
+ }
+
+ @override
+ void visitMapPatternEntry(MapPatternEntry node) {
+ optype.completionLocation = 'MapPatternEntry_value';
+ optype.includeReturnValueSuggestions = true;
+ optype.includeTypeNameSuggestions = true;
+ optype.includeVarNameSuggestions = true;
+ }
+
+ @override
void visitMethodDeclaration(MethodDeclaration node) {
if (identical(entity, node.returnType) ||
identical(entity, node.name) && node.returnType == null) {
To view, visit change 281466. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Konstantin Shcheglov.
Patch set 1:Commit-Queue +1
Attention is currently required from: Brian Wilkerson.
Patch set 1:Code-Review +1
4 comments:
File pkg/analysis_server/lib/src/services/completion/dart/keyword_contributor.dart:
Patch Set #1, Line 640: _addSuggestions
Should not happen in variable declaration context, e.g. `final (var a) = 0;` using `var` is invalid.
File pkg/analysis_server/test/services/completion/dart/location/map_pattern_test.dart:
What's the point of suggesting both top-level and local elements?
Why not also constants of the enclosing class?
From import libraries? With a prefix?
We get something suggested, this should be enough, I think.
Just top-levels or locals.
BTW, if we intent to check the precedence, we should prefer constants first, and using `c01` vs. `v01` is not representative - we could have just sorted by name :-)
Patch Set #1, Line 136: test_entry_key_only
Sigh... Four copies of the same declarations context, and the same results :-(
Just with a different location in a slightly different map pattern.
File pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart:
Patch Set #1, Line 982: MapPatternEntry
Do we care if we are in the key or in the value?
To view, visit change 281466. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Brian Wilkerson.
go/dart-cbuild result: SUCCESS
Details: https://goto.google.com/dart-cbuild/find/d0ef52ffc511871398a5b5f3ae050908bfc0d52e
Patch set 2:Commit-Queue +1
4 comments:
File pkg/analysis_server/lib/src/services/completion/dart/keyword_contributor.dart:
Patch Set #1, Line 640: _addSuggestions
Should not happen in variable declaration context, e.g. `final (var a) = 0;` using `var` is invalid.
I added a test, and we don't suggest `final` or `var` in that context (though I used a map pattern rather than a record pattern in the test).
File pkg/analysis_server/test/services/completion/dart/location/map_pattern_test.dart:
Just top-levels or locals.
I removed the top-level variables.
... if we intent to check the precedence ...
We don't. The relevance tests are separate.
Patch Set #1, Line 136: test_entry_key_only
Sigh... Four copies of the same declarations context, and the same results :-( […]
Maybe not quite as bad with the top-level variables removed.
File pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart:
Patch Set #1, Line 982: MapPatternEntry
Do we care if we are in the key or in the value?
I have not yet found a test case in which it matters. It appears that if we're in the key we always visit the map pattern, not the entry. As a result I've changed the completion location for map patterns.
To view, visit change 281466. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Brian Wilkerson.
Patch set 2:Code-Review +1
1 comment:
Patchset:
LGTM
To view, visit change 281466. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Brian Wilkerson.
go/dart-cbuild result: SUCCESS
Details: https://goto.google.com/dart-cbuild/find/580395aef11e2e66bd09c6671fd992c19c4fb721
Attention is currently required from: Brian Wilkerson.
Patch set 2:Commit-Queue +2
Commit Queue submitted this change.
Add completion support for map patterns
Change-Id: I8560ecdabb484290066b6cc4cb2a77f72163a995
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/281466
Reviewed-by: Konstantin Shcheglov <sche...@google.com>
Commit-Queue: Brian Wilkerson <brianwi...@google.com>
---
M pkg/analysis_server/lib/src/services/completion/dart/keyword_contributor.dart
A pkg/analysis_server/test/services/completion/dart/location/map_pattern_test.dart
M pkg/analysis_server/test/services/completion/dart/location/test_all.dart
M pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
4 files changed, 414 insertions(+), 0 deletions(-)
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/keyword_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/keyword_contributor.dart
index 85f1c01..81d3ce1 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/keyword_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/keyword_contributor.dart
@@ -630,6 +630,19 @@
}
@override
+ void visitMapPattern(MapPattern node) {
+ _addConstantExpressionKeywords(node);
+ super.visitMapPattern(node);
+ }
+
+ @override
+ void visitMapPatternEntry(MapPatternEntry node) {
+ _addSuggestions([Keyword.FINAL, Keyword.VAR]);
+ _addExpressionKeywords(node);
+ super.visitMapPatternEntry(node);
+ }
+
+ @override
void visitMethodDeclaration(MethodDeclaration node) {
if (entity == node.body) {
if (node.body.isEmpty) {
diff --git a/pkg/analysis_server/test/services/completion/dart/location/map_pattern_test.dart b/pkg/analysis_server/test/services/completion/dart/location/map_pattern_test.dart
new file mode 100644
index 0000000..5966e66
--- /dev/null
+++ b/pkg/analysis_server/test/services/completion/dart/location/map_pattern_test.dart
@@ -0,0 +1,370 @@
+ Future<void> test_entry_key_assignmentContext_middle() async {
+ await computeSuggestions('''
+void f(Map<String, int> m1) {
+ const c1 = '3';
+ var v1 = '4';
+ final {c1 : 1, ^, c1 : 3} = m1;
+}
+''');
+ assertResponse('''
+suggestions
+ c1
+ kind: localVariable
+ v1
+ kind: localVariable
+ false
+ kind: keyword
+ m1
+ kind: parameter
+ null
+ kind: keyword
+ true
+ kind: keyword
+ const
+ kind: keyword
+''');
+ }
+
+ Future<void> test_entry_key_assignmentContext_only() async {
+ await computeSuggestions('''
+void f(Map<String, int> m1) {
+ const c1 = '3';
+ var v1 = '4';
+ final {^} = m1;
+}
+''');
+ assertResponse('''
+suggestions
+ c1
+ kind: localVariable
+ v1
+ kind: localVariable
+ false
+ kind: keyword
+ m1
+ kind: parameter
+ null
+ kind: keyword
+ true
+ kind: keyword
+ const
+ kind: keyword
+''');
+ }
+
+ Future<void> test_entry_key_first() async {
+ await computeSuggestions('''
+void f(Object o1) {
+ const c1 = '3';
+ var v1 = '4';
+ switch (o1) {
+ case <String, int>{^ c1 : 1, c1 : 3]:
+ }
+}
+''');
+ assertResponse('''
+suggestions
+ c1
+ kind: localVariable
+ v1
+ kind: localVariable
+ false
+ kind: keyword
+ null
+ kind: keyword
+ true
+ kind: keyword
+ o1
+ kind: parameter
+ const
+ kind: keyword
+''');
+ }
+
+ Future<void> test_entry_key_last() async {
+ await computeSuggestions('''
+void f(Object o1) {
+ const c1 = '3';
+ var v1 = '4';
+ switch (o1) {
+ case <String, int>{c1 : 1, c1 : 3, ^}:
+ }
+}
+''');
+ assertResponse('''
+suggestions
+ c1
+ kind: localVariable
+ v1
+ kind: localVariable
+ false
+ kind: keyword
+ null
+ kind: keyword
+ true
+ kind: keyword
+ o1
+ kind: parameter
+ const
+ kind: keyword
+''');
+ }
+
+ Future<void> test_entry_key_middle() async {
+ await computeSuggestions('''
+void f(Object o1) {
+ const c1 = '3';
+ var v1 = '4';
+ switch (o1) {
+ case <String, int>{c1 : 1, ^, c1 : 3}:
+ }
+}
+''');
+ assertResponse('''
+suggestions
+ c1
+ kind: localVariable
+ v1
+ kind: localVariable
+ false
+ kind: keyword
+ null
+ kind: keyword
+ true
+ kind: keyword
+ o1
+ kind: parameter
+ const
+ kind: keyword
+''');
+ }
+
+ Future<void> test_entry_key_middle_partial() async {
+ await computeSuggestions('''
+void f(Object o1) {
+ const c1 = '3';
+ var v1 = '4';
+ switch (o1) {
+ case <String, int>{c1 : 1, c^, c1 : 3}:
+ }
+}
+''');
+ if (isProtocolVersion2) {
+ assertResponse('''
+replacement
+ left: 1
+suggestions
+ c1
+ kind: localVariable
+ const
+ kind: keyword
+''');
+ } else {
+ assertResponse('''
+replacement
+ left: 1
+suggestions
+ c1
+ kind: localVariable
+ v1
+ kind: localVariable
+ false
+ kind: keyword
+ null
+ kind: keyword
+ true
+ kind: keyword
+ o1
+ kind: parameter
+ const
+ kind: keyword
+''');
+ }
+ }
+
+ Future<void> test_entry_key_only() async {
+ await computeSuggestions('''
+void f(Object o1) {
+ const c1 = '3';
+ var v1 = '4';
+ switch (o1) {
+ case <String, int>{^}:
+ }
+}
+''');
+ assertResponse('''
+suggestions
+ c1
+ kind: localVariable
+ v1
+ kind: localVariable
+ false
+ kind: keyword
+ null
+ kind: keyword
+ true
+ kind: keyword
+ o1
+ kind: parameter
+ const
+ kind: keyword
+''');
+ }
+
+ Future<void> test_entry_key_only_partial() async {
+ await computeSuggestions('''
+void f(Object o1) {
+ const c1 = '3';
+ var v1 = '4';
+ switch (o1) {
+ case <String, int>{c^}:
+ }
+}
+''');
+ if (isProtocolVersion2) {
+ assertResponse('''
+replacement
+ left: 1
+suggestions
+ c1
+ kind: localVariable
+ const
+ kind: keyword
+''');
+ } else {
+ assertResponse('''
+replacement
+ left: 1
+suggestions
+ c1
+ kind: localVariable
+ v1
+ kind: localVariable
+ false
+ kind: keyword
+ null
+ kind: keyword
+ true
+ kind: keyword
+ o1
+ kind: parameter
+ const
+ kind: keyword
+''');
+ }
+ }
+
+ Future<void> test_entry_value() async {
+ await computeSuggestions('''
+void f(Object o1) {
+ const c1 = '3';
+ var v1 = '4';
+ switch (o1) {
+ case <String, int>{c1 : ^}:
+ }
+}
+''');
+ assertResponse('''
+suggestions
+ c1
+ kind: localVariable
+ false
+ kind: keyword
+ null
+ kind: keyword
+ true
+ kind: keyword
+ v1
+ kind: localVariable
+ o1
+ kind: parameter
+ const
+ kind: keyword
+ final
+ kind: keyword
+ switch
+ kind: keyword
+ var
+ kind: keyword
+''');
+ }
+
+ Future<void> test_entry_value_partial() async {
+ await computeSuggestions('''
+void f(Object o1) {
+ const c1 = '3';
+ var v1 = '4';
+ switch (o1) {
+ case <String, int>{c1 : v^}:
+ }
+}
+''');
+ if (isProtocolVersion2) {
+ assertResponse('''
+replacement
+ left: 1
+suggestions
+ v1
+ kind: localVariable
+ var
+ kind: keyword
+''');
+ } else {
+ assertResponse('''
+replacement
+ left: 1
+suggestions
+ c1
+ kind: localVariable
+ false
+ kind: keyword
+ null
+ kind: keyword
+ true
+ kind: keyword
+ v1
index 5bb191b..4058131 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
@@ -970,6 +970,23 @@
}
@override
+ void visitMapPattern(MapPattern node) {
+ optype.completionLocation = 'MapPatternEntry_key';
+ optype.includeReturnValueSuggestions = true;
+ optype.includeTypeNameSuggestions = true;
+ optype.includeVarNameSuggestions = true;
+ optype.mustBeConst = true;
+ }
+
+ @override
+ void visitMapPatternEntry(MapPatternEntry node) {
+ optype.completionLocation = 'MapPatternEntry_value';
+ optype.includeReturnValueSuggestions = true;
+ optype.includeTypeNameSuggestions = true;
+ optype.includeVarNameSuggestions = true;
+ }
+
+ @override
void visitMethodDeclaration(MethodDeclaration node) {
if (identical(entity, node.returnType) ||
identical(entity, node.name) && node.returnType == null) {
To view, visit change 281466. To unsubscribe, or for help writing mail filters, visit settings.
go/dart-cbuild result: SUCCESS
Details: https://goto.google.com/dart-cbuild/find/872a6a4caa0c697b98d6b189f2ccfee64bac71d8