Skip to content

Commit 587cbed

Browse files
bwilkersonCommit Queue
authored andcommitted
Convert the completion metrics tool to use candidate suggestions
The tool was previously using the old implementation, which is now only used by the Cider support. As a result, it wasn't providing valid timing measurements for the new implementation. That's been fixed. The map/reduce functionality is not yet supported, but it's also rarely used. There's some information missing, but most of the functionality works as expected, and I'd like to tackle the rest in a separate CL (though I don't know exactly when I'll get to it). Change-Id: I98150c1d5273a8ffaccf7858f9588e52e11636c8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/444183 Reviewed-by: Samuel Rawlins <[email protected]> Commit-Queue: Brian Wilkerson <[email protected]>
1 parent d34b082 commit 587cbed

File tree

4 files changed

+52
-124
lines changed

4 files changed

+52
-124
lines changed

pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'package:analysis_server/src/protocol_server.dart'
77
hide Element, ElementKind;
88
import 'package:analysis_server/src/protocol_server.dart' as protocol;
99
import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
10+
import 'package:analysis_server/src/services/completion/dart/candidate_suggestion.dart';
1011
import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
1112
import 'package:analysis_server/src/services/completion/dart/dart_completion_suggestion.dart';
1213
import 'package:analysis_server/src/services/completion/dart/relevance_computer.dart';
@@ -1585,6 +1586,9 @@ class SuggestionBuilder {
15851586
}
15861587

15871588
abstract class SuggestionListener {
1589+
/// Invoked when a candidate suggestion has had its relevance score computed.
1590+
void builtCandidate(CandidateSuggestion candidate);
1591+
15881592
/// Invoked when a suggestion has been built.
15891593
void builtSuggestion(CompletionSuggestionBuilder suggestionBuilder);
15901594

pkg/analysis_server/lib/src/services/completion/dart/suggestion_collector.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ class SuggestionCollector {
9393
computer.completionLocation = completionLocation;
9494
for (var candidate in suggestions) {
9595
candidate.relevanceScore = computer.computeRelevance(candidate);
96+
computer.listener?.builtCandidate(candidate);
9697
}
9798
// Sort the suggestions based on both the matcher score and the relevance
9899
// score.

pkg/analysis_server/tool/code_completion/completion_metrics.dart

Lines changed: 41 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,13 @@ import 'dart:io' show stdout;
1111
import 'dart:math' as math;
1212

1313
import 'package:_fe_analyzer_shared/src/base/syntactic_entity.dart';
14-
import 'package:analysis_server/src/protocol/protocol_internal.dart';
1514
import 'package:analysis_server/src/protocol_server.dart' as protocol;
15+
import 'package:analysis_server/src/services/completion/dart/candidate_suggestion.dart';
1616
import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
1717
import 'package:analysis_server/src/services/completion/dart/feature_computer.dart';
1818
import 'package:analysis_server/src/services/completion/dart/probability_range.dart';
1919
import 'package:analysis_server/src/services/completion/dart/relevance_tables.g.dart';
2020
import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
21-
import 'package:analysis_server/src/services/completion/dart/utilities.dart';
2221
import 'package:analysis_server/src/status/pages.dart';
2322
import 'package:analyzer/dart/analysis/analysis_context.dart';
2423
import 'package:analyzer/dart/analysis/results.dart';
@@ -577,7 +576,7 @@ class CompletionMetrics {
577576
void recordShadowedCompletion(
578577
String? completionLocation,
579578
ExpectedCompletion expectedCompletion,
580-
CompletionSuggestionLite closeMatchSuggestion,
579+
CandidateSuggestion closeMatchSuggestion,
581580
) {
582581
shadowedCompletions
583582
.putIfAbsent(completionLocation ?? 'unknown', () => [])
@@ -935,7 +934,7 @@ class CompletionQualityMetricsComputer extends CompletionMetricsComputer {
935934
MetricsSuggestionListener listener,
936935
ExpectedCompletion expectedCompletion,
937936
String? completionLocation,
938-
List<CompletionSuggestionLite> suggestions,
937+
List<CandidateSuggestion> suggestions,
939938
CompletionMetrics metrics,
940939
int elapsedMS,
941940
) {
@@ -965,7 +964,7 @@ class CompletionQualityMetricsComputer extends CompletionMetricsComputer {
965964
.toList();
966965
precedingRelevanceCounts = <int, int>{};
967966
for (var i = 0; i < rank - 1; i++) {
968-
var relevance = suggestions[i].relevance;
967+
var relevance = suggestions[i].relevanceScore;
969968
precedingRelevanceCounts[relevance] =
970969
(precedingRelevanceCounts[relevance] ?? 0) + 1;
971970
}
@@ -1008,7 +1007,7 @@ class CompletionQualityMetricsComputer extends CompletionMetricsComputer {
10081007

10091008
if (options.printMissedCompletionDetails ||
10101009
options.printShadowedCompletionDetails) {
1011-
CompletionSuggestionLite? closeMatchSuggestion;
1010+
CandidateSuggestion? closeMatchSuggestion;
10121011
for (var suggestion in suggestions) {
10131012
if (suggestion.completion == expectedCompletion.completion) {
10141013
closeMatchSuggestion = suggestion;
@@ -1488,7 +1487,7 @@ class CompletionQualityMetricsComputer extends CompletionMetricsComputer {
14881487

14891488
int _computeCharsBeforeTop(
14901489
ExpectedCompletion target,
1491-
List<CompletionSuggestionLite> suggestions, {
1490+
List<CandidateSuggestion> suggestions, {
14921491
int minRank = 1,
14931492
}) {
14941493
var rank = placementInSuggestionList(suggestions, target).rank;
@@ -1509,34 +1508,28 @@ class CompletionQualityMetricsComputer extends CompletionMetricsComputer {
15091508

15101509
/// Computes completion suggestions for [dartRequest], and returns the
15111510
/// suggestions, sorted by rank and then by completion text.
1512-
Future<List<CompletionSuggestionLite>> _computeCompletionSuggestions(
1511+
Future<List<CandidateSuggestion>> _computeCompletionSuggestions(
15131512
MetricsSuggestionListener listener,
15141513
OperationPerformanceImpl performance,
15151514
DartCompletionRequest dartRequest,
15161515
NotImportedSuggestions notImportedSuggestions,
15171516
) async {
15181517
var budget = CompletionBudget(Duration(seconds: 30));
1519-
var suggestions = await DartCompletionManager(
1518+
var collector = await DartCompletionManager(
15201519
budget: budget,
15211520
listener: listener,
15221521
notImportedSuggestions: notImportedSuggestions,
1523-
).computeSuggestions(
1524-
dartRequest,
1525-
performance,
1522+
).computeFinalizedCandidateSuggestions(
1523+
request: dartRequest,
1524+
performance: performance,
15261525
maxSuggestions: -1,
1527-
useFilter: true,
15281526
);
1529-
1530-
// Note that some routes sort suggestions before responding differently.
1531-
// The Cider and legacy handlers use [fuzzyFilterSort], which does not match
1532-
// [completionComparator].
1533-
suggestions.sort(completionComparator);
1534-
return suggestions.map(CompletionSuggestionLite.fromBuilder).toList();
1527+
return collector.suggestions;
15351528
}
15361529

1537-
List<CompletionSuggestionLite> _filterSuggestions(
1530+
List<CandidateSuggestion> _filterSuggestions(
15381531
String prefix,
1539-
List<CompletionSuggestionLite> suggestions,
1532+
List<CandidateSuggestion> suggestions,
15401533
) {
15411534
// TODO(brianwilkerson): Replace this with a more realistic filtering
15421535
// algorithm.
@@ -1550,7 +1543,7 @@ class CompletionQualityMetricsComputer extends CompletionMetricsComputer {
15501543
var suggestion = data.suggestion;
15511544
return [
15521545
rank.toString(),
1553-
suggestion.relevance.toString(),
1546+
suggestion.relevanceScore.toString(),
15541547
suggestion.completion,
15551548
suggestion.kind.toString(),
15561549
];
@@ -1635,7 +1628,7 @@ class CompletionQualityMetricsComputer extends CompletionMetricsComputer {
16351628
///
16361629
/// If [expectedCompletion] is not found, `Place.none()` is returned.
16371630
static Place placementInSuggestionList(
1638-
List<CompletionSuggestionLite> suggestions,
1631+
List<CandidateSuggestion> suggestions,
16391632
ExpectedCompletion expectedCompletion,
16401633
) {
16411634
for (var i = 0; i < suggestions.length; i++) {
@@ -1824,79 +1817,6 @@ class CompletionResult {
18241817
}
18251818
}
18261819

1827-
/// Enough data from [CompletionSuggestionBuilder] to compute metrics.
1828-
///
1829-
/// It excludes expensive parts that are not necessary, such as element
1830-
/// properties.
1831-
class CompletionSuggestionLite {
1832-
final String completion;
1833-
final protocol.ElementKind? elementKind;
1834-
final int relevance;
1835-
final protocol.CompletionSuggestionKind kind;
1836-
1837-
CompletionSuggestionLite({
1838-
required this.completion,
1839-
required this.elementKind,
1840-
required this.relevance,
1841-
required this.kind,
1842-
});
1843-
1844-
factory CompletionSuggestionLite.fromBuilder(
1845-
CompletionSuggestionBuilder builder,
1846-
) {
1847-
return CompletionSuggestionLite(
1848-
completion: builder.completion,
1849-
elementKind: builder.elementKind,
1850-
relevance: builder.relevance,
1851-
kind: builder.kind,
1852-
);
1853-
}
1854-
1855-
factory CompletionSuggestionLite.fromJson(Map<String, Object?> map) {
1856-
var elementKindStr = map['elementKind'] as String?;
1857-
1858-
return CompletionSuggestionLite(
1859-
completion: map['completion'] as String,
1860-
elementKind:
1861-
elementKindStr != null
1862-
? protocol.ElementKind.fromJson(
1863-
ResponseDecoder(null),
1864-
'',
1865-
elementKindStr,
1866-
)
1867-
: null,
1868-
relevance: map['relevance'] as int,
1869-
kind: protocol.CompletionSuggestionKind.fromJson(
1870-
ResponseDecoder(null),
1871-
'',
1872-
map['kind'] as String,
1873-
),
1874-
);
1875-
}
1876-
1877-
@override
1878-
int get hashCode => Object.hash(completion, kind);
1879-
1880-
@override
1881-
bool operator ==(Object other) {
1882-
return other is CompletionSuggestionLite &&
1883-
other.completion == completion &&
1884-
other.kind == kind &&
1885-
other.elementKind == elementKind &&
1886-
other.relevance == relevance;
1887-
}
1888-
1889-
/// Return a map used to represent this suggestion data in a JSON structure.
1890-
Map<String, Object?> toJson() {
1891-
return {
1892-
'completion': completion,
1893-
if (elementKind case var elementKind?) 'elementKind': elementKind.name,
1894-
'relevance': relevance,
1895-
'kind': kind.name,
1896-
};
1897-
}
1898-
}
1899-
19001820
/// The data to be printed on a single line in the table of mrr values per
19011821
/// completion location.
19021822
class LocationTableLine {
@@ -1931,7 +1851,7 @@ class MetricsSuggestionListener implements SuggestionListener {
19311851
0.0,
19321852
];
19331853

1934-
Map<CompletionSuggestionLite, List<double>> featureMap = Map.identity();
1854+
Map<CandidateSuggestion, List<double>> featureMap = Map.identity();
19351855

19361856
List<double> cachedFeatures = noFeatures;
19371857

@@ -1940,12 +1860,16 @@ class MetricsSuggestionListener implements SuggestionListener {
19401860
String? missingCompletionLocationTable;
19411861

19421862
@override
1943-
void builtSuggestion(CompletionSuggestionBuilder builder) {
1944-
var suggestion = CompletionSuggestionLite.fromBuilder(builder);
1945-
featureMap[suggestion] = cachedFeatures;
1863+
void builtCandidate(CandidateSuggestion candidate) {
1864+
featureMap[candidate] = cachedFeatures;
19461865
cachedFeatures = noFeatures;
19471866
}
19481867

1868+
@override
1869+
void builtSuggestion(CompletionSuggestionBuilder builder) {
1870+
throw UnsupportedError('Unexpected use of SuggestionBuilder');
1871+
}
1872+
19491873
@override
19501874
void computedFeatures({
19511875
double contextType = 0.0,
@@ -2020,15 +1944,15 @@ class RelevanceTables {
20201944
class ShadowedCompletion {
20211945
final ExpectedCompletion expectedCompletion;
20221946

2023-
final CompletionSuggestionLite closeMatchSuggestion;
1947+
final CandidateSuggestion closeMatchSuggestion;
20241948

20251949
ShadowedCompletion(this.expectedCompletion, this.closeMatchSuggestion);
20261950
}
20271951

20281952
/// The information being remembered about an individual suggestion.
20291953
class SuggestionData {
20301954
/// The suggestion that was produced.
2031-
CompletionSuggestionLite suggestion;
1955+
CandidateSuggestion suggestion;
20321956

20331957
/// The values of the features used to compute the suggestion.
20341958
List<double> features;
@@ -2037,17 +1961,17 @@ class SuggestionData {
20371961

20381962
/// Return an instance extracted from the decoded JSON [map].
20391963
factory SuggestionData.fromJson(Map<String, dynamic> map) {
2040-
return SuggestionData(
2041-
CompletionSuggestionLite.fromJson(
2042-
map['suggestion'] as Map<String, Object?>,
2043-
),
2044-
(map['features'] as List<dynamic>).cast<double>(),
2045-
);
1964+
throw UnimplementedError();
1965+
// return SuggestionData(
1966+
// CandidateSuggestion.fromJson(map['suggestion'] as Map<String, Object?>),
1967+
// (map['features'] as List<dynamic>).cast<double>(),
1968+
// );
20461969
}
20471970

20481971
/// Return a map used to represent this suggestion data in a JSON structure.
20491972
Map<String, dynamic> toJson() {
2050-
return {'suggestion': suggestion.toJson(), 'features': features};
1973+
throw UnimplementedError();
1974+
// return {'suggestion': suggestion.toJson(), 'features': features};
20511975
}
20521976
}
20531977

@@ -2094,6 +2018,14 @@ extension on CompletionGroup {
20942018
}
20952019
}
20962020

2021+
extension on CandidateSuggestion {
2022+
/// The kind of element being suggested.
2023+
protocol.CompletionSuggestionKind? get kind {
2024+
// TODO(brianwilkerson): Implement this.
2025+
return null;
2026+
}
2027+
}
2028+
20972029
extension on num {
20982030
String asPercentage([int fractionDigits = 1]) =>
20992031
'${(this * 100).toStringAsFixed(fractionDigits)}%'.padLeft(

pkg/analysis_server/tool/code_completion/visitors.dart

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

55
import 'package:analysis_server/src/protocol/protocol_internal.dart';
66
import 'package:analysis_server/src/protocol_server.dart' as protocol;
7+
import 'package:analysis_server/src/services/completion/dart/candidate_suggestion.dart';
78
import 'package:analyzer/dart/analysis/results.dart';
89
import 'package:analyzer/dart/ast/ast.dart';
910
import 'package:analyzer/dart/ast/syntactic_entity.dart';
@@ -13,8 +14,6 @@ import 'package:analyzer/dart/element/element.dart' as element;
1314
import 'package:analyzer/dart/element/type.dart';
1415
import 'package:analyzer/source/line_info.dart';
1516

16-
import 'completion_metrics.dart';
17-
1817
class ExpectedCompletion {
1918
final String _filePath;
2019

@@ -105,19 +104,11 @@ class ExpectedCompletion {
105104

106105
SyntacticEntity get syntacticEntity => _entity;
107106

108-
bool matches(CompletionSuggestionLite completionSuggestion) {
109-
if (completionSuggestion.completion == completion) {
110-
if (kind != null && completionSuggestion.kind != kind) {
111-
return false;
112-
}
113-
if (elementKind != null &&
114-
completionSuggestion.elementKind != null &&
115-
completionSuggestion.elementKind != elementKind) {
116-
return false;
117-
}
118-
return true;
119-
}
120-
return false;
107+
bool matches(CandidateSuggestion completionSuggestion) {
108+
// This method used to use the `_kind` and `_elementKind` to further
109+
// validate that the right suggestion had been found, but that was removed
110+
// because candidate suggestions don't have that information.
111+
return completionSuggestion.completion == completion;
121112
}
122113

123114
/// Return a map used to represent this expected completion in a JSON structure.

0 commit comments

Comments
 (0)