Issue 49054. Use prefix filter for UriBasedDirective, use MatchStyle.FILENAME

Bug: https://github.com/dart-lang/sdk/issues/49054
Change-Id: I127944d9eca20f0d8a57e26b3050fcfcb381d2b1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/245623
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart b/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
index 33bc64a..8e1cc46 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
@@ -473,6 +473,19 @@
       }
     }
 
+    if (entity is Token &&
+        entity.type == TokenType.STRING &&
+        entity.offset < offset &&
+        offset < entity.end) {
+      final uriNode = target.containingNode;
+      if (uriNode is SimpleStringLiteral && uriNode.literal == entity) {
+        final directive = uriNode.parent;
+        if (directive is UriBasedDirective && directive.uri == uriNode) {
+          return uriNode.value.substring(0, offset - uriNode.contentsOffset);
+        }
+      }
+    }
+
     while (entity is AstNode) {
       if (entity is SimpleIdentifier) {
         var identifier = entity.name;
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/fuzzy_filter_sort.dart b/pkg/analysis_server/lib/src/services/completion/dart/fuzzy_filter_sort.dart
index 1fbda62..f410bf1 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/fuzzy_filter_sort.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/fuzzy_filter_sort.dart
@@ -5,6 +5,7 @@
 import 'package:analysis_server/src/protocol_server.dart';
 import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
 import 'package:analysis_server/src/services/completion/filtering/fuzzy_matcher.dart';
+import 'package:collection/collection.dart';
 
 final _identifierPattern = RegExp(r'([_a-zA-Z][_a-zA-Z0-9]*)');
 
@@ -14,7 +15,11 @@
   required String pattern,
   required List<CompletionSuggestionBuilder> suggestions,
 }) {
-  var matcher = FuzzyMatcher(pattern, matchStyle: MatchStyle.SYMBOL);
+  final matchStyle =
+      suggestions.firstOrNull?.kind == CompletionSuggestionKind.IMPORT
+          ? MatchStyle.FILENAME
+          : MatchStyle.SYMBOL;
+  final matcher = FuzzyMatcher(pattern, matchStyle: matchStyle);
 
   double score(CompletionSuggestionBuilder suggestion) {
     var textToMatch = suggestion.textToMatch;
diff --git a/pkg/analysis_server/test/client/impl/completion_driver.dart b/pkg/analysis_server/test/client/impl/completion_driver.dart
index 6695249..c6b01d4 100644
--- a/pkg/analysis_server/test/client/impl/completion_driver.dart
+++ b/pkg/analysis_server/test/client/impl/completion_driver.dart
@@ -276,6 +276,8 @@
     } else if (notification.event == ANALYSIS_NOTIFICATION_ERRORS) {
       var decoded = AnalysisErrorsParams.fromNotification(notification);
       filesErrors[decoded.file] = decoded.errors;
+    } else if (notification.event == ANALYSIS_NOTIFICATION_FLUSH_RESULTS) {
+      // Ignored.
     } else if (notification.event == SERVER_NOTIFICATION_ERROR) {
       throw Exception('server error: ${notification.toJson()}');
     } else if (notification.event == SERVER_NOTIFICATION_CONNECTED) {
diff --git a/pkg/analysis_server/test/services/completion/dart/completion_check.dart b/pkg/analysis_server/test/services/completion/dart/completion_check.dart
index ff98794..242212c 100644
--- a/pkg/analysis_server/test/services/completion/dart/completion_check.dart
+++ b/pkg/analysis_server/test/services/completion/dart/completion_check.dart
@@ -220,6 +220,10 @@
     element.isNotNull.kind.isGetter;
   }
 
+  void get isImport {
+    kind.isImport;
+  }
+
   void get isImportPrefix {
     kind.isIdentifier;
     element.isNotNull.kind.isPrefix;
@@ -373,6 +377,10 @@
     isEqualTo(CompletionSuggestionKind.IDENTIFIER);
   }
 
+  void get isImport {
+    isEqualTo(CompletionSuggestionKind.IMPORT);
+  }
+
   void get isInvocation {
     isEqualTo(CompletionSuggestionKind.INVOCATION);
   }
diff --git a/pkg/analysis_server/test/services/completion/dart/location/directive_uri_test.dart b/pkg/analysis_server/test/services/completion/dart/location/directive_uri_test.dart
new file mode 100644
index 0000000..1c1ceb2
--- /dev/null
+++ b/pkg/analysis_server/test/services/completion/dart/location/directive_uri_test.dart
@@ -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/src/test_utilities/package_config_file_builder.dart';
+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(DirectiveUriTest);
+  });
+}
+
+@reflectiveTest
+class DirectiveUriTest extends AbstractCompletionDriverTest {
+  @override
+  TestingCompletionProtocol get protocol => TestingCompletionProtocol.version2;
+
+  Future<void> test_uri_end() async {
+    await _checkDirectives(
+      uriContent: 'foo0^',
+      validator: (response) {
+        // We have both `foo0x`, but no `bar`.
+        check(response).suggestions.matchesInAnyOrder([
+          (suggestion) => suggestion
+            ..isImport
+            ..completion.isEqualTo('package:foo/foo01.dart'),
+          (suggestion) => suggestion
+            ..isImport
+            ..completion.isEqualTo('package:foo/foo02.dart'),
+        ]);
+      },
+    );
+  }
+
+  Future<void> test_uri_notEnd() async {
+    await _checkDirectives(
+      uriContent: 'foo0^xyz',
+      validator: (response) {
+        // We ignore 'xyz' after the caret.
+        check(response).suggestions.matchesInAnyOrder([
+          (suggestion) => suggestion
+            ..isImport
+            ..completion.isEqualTo('package:foo/foo01.dart'),
+          (suggestion) => suggestion
+            ..isImport
+            ..completion.isEqualTo('package:foo/foo02.dart'),
+        ]);
+      },
+    );
+  }
+
+  Future<void> _checkDirectives({
+    required String uriContent,
+    required void Function(CompletionResponseForTesting response) validator,
+  }) async {
+    _configurePackagesFooBar();
+
+    {
+      var response = await getTestCodeSuggestions('''
+export '$uriContent';
+''');
+      validator(response);
+    }
+
+    {
+      var response = await getTestCodeSuggestions('''
+import '$uriContent';
+''');
+      validator(response);
+    }
+  }
+
+  void _configurePackagesFooBar() {
+    final fooPackageRoot = getFolder('$packagesRootPath/foo');
+    newFile('$packagesRootPath/foo/lib/foo01.dart', '');
+    newFile('$packagesRootPath/foo/lib/foo02.dart', '');
+    // We use this file to check that exactly `foo0` is used as prefix.
+    // So, we don't have one-off and don't use just `foo`.
+    newFile('$packagesRootPath/foo/lib/foo11.dart', '');
+
+    final barPackageRoot = getFolder('$packagesRootPath/bar');
+    newFile('$packagesRootPath/bar/lib/bar01.dart', '');
+
+    writeTestPackageConfig(
+      config: PackageConfigFileBuilder()
+        ..add(name: 'foo', rootPath: fooPackageRoot.path)
+        ..add(name: 'bar', rootPath: barPackageRoot.path),
+    );
+  }
+}
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 b0fe488..467a484 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
@@ -6,6 +6,7 @@
 
 import 'class_body_test.dart' as class_body;
 import 'compilation_unit_test.dart' as compilation_unit;
+import 'directive_uri_test.dart' as directive_uri;
 import 'enum_constant_test.dart' as enum_constant;
 import 'enum_test.dart' as enum_;
 import 'field_formal_parameter_test.dart' as field_formal_parameter;
@@ -17,6 +18,7 @@
   defineReflectiveSuite(() {
     class_body.main();
     compilation_unit.main();
+    directive_uri.main();
     enum_constant.main();
     enum_.main();
     field_formal_parameter.main();