Files
sdk/pkg/analysis_server/test/lsp/completion.dart
T
Danny Tuppeny 20d598c7ce [analysis_server] Change LSP server to truncate using Fuzzy Score before Relevance
Previously the fuzzy score was only used for filtering, then we truncated using the relevance score. This change uses the fuzzy score for sorting prior to truncation (calling back to relevance if it's the same) too.

Currently, VS Code in non-web contexts does not truncate (it's set to 100,000 items) so this change will only impact web contexts (currently set to 1,000 items to reduce payload sizes). From my testing, the difference in the first 30 items that will be seen is negligible (there are some differences between the servers fuzzy score and VS Codes, but they tend to be when there are typos).

Change-Id: Ia10d1991085054cec9c0c1071a516c8aa286156c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/329323
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
2023-10-09 17:05:08 +00:00

113 lines
3.7 KiB
Dart

// Copyright (c) 2021, 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:analysis_server/lsp_protocol/protocol.dart';
import 'package:analyzer/src/test_utilities/test_code_format.dart';
import 'package:test/test.dart';
import '../utils/test_code_extensions.dart';
import 'server_abstract.dart';
mixin CompletionTestMixin on AbstractLspAnalysisServerTest {
/// The last set of completion results fetched.
List<CompletionItem> completionResults = [];
int sortTextSorter(CompletionItem item1, CompletionItem item2) =>
(item1.sortText ?? item1.label).compareTo(item2.sortText ?? item2.label);
Future<String?> verifyCompletions(
Uri fileUri,
String content, {
required List<String> expectCompletions,
String? applyEditsFor,
bool resolve = false,
String? expectedContent,
String? expectedContentIfInserting,
bool verifyInsertReplaceRanges = false,
bool openCloseFile = true,
bool expectNoAdditionalItems = false,
}) async {
final code = TestCode.parse(content);
// If verifyInsertReplaceRanges is true, we need both expected contents.
assert(verifyInsertReplaceRanges == false ||
(expectedContent != null && expectedContentIfInserting != null));
if (!initialized) {
setCompletionItemSnippetSupport();
if (verifyInsertReplaceRanges) {
setCompletionItemInsertReplaceSupport();
}
await initialize();
}
if (openCloseFile) {
await openFile(fileUri, code.code);
}
completionResults = await getCompletion(fileUri, code.position.position);
if (openCloseFile) {
await closeFile(fileUri);
}
final sortedResults = completionResults
// Filter to those we expect (unless `expectNoAdditionalItems` which
// indicates the test wants to ensure no additional unmatched items)
.where((r) =>
expectNoAdditionalItems || expectCompletions.contains(r.label))
.toList()
// Sort using sortText as a client would.
..sort(sortTextSorter);
expect(sortedResults.map((item) => item.label), equals(expectCompletions));
// Check the edits apply correctly.
if (applyEditsFor != null) {
var item = completionResults.singleWhere((c) => c.label == applyEditsFor);
final insertFormat = item.insertTextFormat;
if (resolve) {
item = await resolveCompletion(item);
}
String updatedContent;
if (verifyInsertReplaceRanges &&
expectedContent != expectedContentIfInserting) {
// Replacing.
updatedContent = applyTextEdits(
code.code,
[textEditForReplace(item.textEdit!)],
);
expect(
withCaret(updatedContent, insertFormat), equals(expectedContent));
// Inserting.
final inserted = applyTextEdits(
code.code,
[textEditForInsert(item.textEdit!)],
);
expect(withCaret(inserted, insertFormat),
equals(expectedContentIfInserting));
} else {
updatedContent = applyTextEdits(
code.code,
[toTextEdit(item.textEdit!)],
);
if (expectedContent != null) {
expect(
withCaret(updatedContent, insertFormat), equals(expectedContent));
}
}
return updatedContent;
}
return null;
}
/// Replaces the LSP snippet placeholder '$0' with '^' for easier verifying
/// of the cursor position in completions.
String withCaret(String contents, InsertTextFormat? format) =>
format == InsertTextFormat.Snippet
? contents.replaceFirst(r'$0', '^')
: contents;
}