Skip to content

Commit 94abcc8

Browse files
scheglovCommit Queue
authored andcommitted
Completion. Use CompletionSuggestionBuilder in completion metrics, faster.
For sdk/pkg/analyzer_cli Before: 0:01:32.542000 After: 0:00:33.254000 So, about 3 times faster. Change-Id: I8eae44eeefd5624f88a42fe37b8ae1c29427ee49 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/356310 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Konstantin Shcheglov <[email protected]>
1 parent 1e17df5 commit 94abcc8

File tree

4 files changed

+111
-30
lines changed

4 files changed

+111
-30
lines changed

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ abstract class CompletionSuggestionBuilder {
3131
/// See [CompletionSuggestion.completion].
3232
String get completion;
3333

34+
/// The kind of the element, if there is the associated element.
35+
/// We use it for completion metrics, to avoid [build].
36+
protocol.ElementKind? get elementKind;
37+
3438
/// The key used to de-duplicate suggestions.
3539
String get key => completion;
3640

@@ -1669,6 +1673,9 @@ class ValueCompletionSuggestionBuilder implements CompletionSuggestionBuilder {
16691673
@override
16701674
String get completion => _suggestion.completion;
16711675

1676+
@override
1677+
protocol.ElementKind? get elementKind => _suggestion.element?.kind;
1678+
16721679
@override
16731680
String get key => completion;
16741681

@@ -1716,6 +1723,9 @@ class _CompletionSuggestionBuilderImpl implements CompletionSuggestionBuilder {
17161723
required this.isNotImported,
17171724
});
17181725

1726+
@override
1727+
protocol.ElementKind? get elementKind => convertElementKind(orgElement.kind);
1728+
17191729
// TODO(scheglov): implement better key for not-yet-imported
17201730
@override
17211731
String get key {

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
/// A collection of utility methods used by completion contributors.
66
library;
77

8-
import 'package:analysis_server/src/protocol_server.dart'
9-
show CompletionSuggestion, Location;
8+
import 'package:analysis_server/src/protocol_server.dart' show Location;
109
import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
10+
import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
1111
import 'package:analysis_server/src/utilities/extensions/ast.dart';
1212
import 'package:analyzer/dart/analysis/code_style_options.dart';
1313
import 'package:analyzer/dart/ast/ast.dart';
@@ -23,7 +23,7 @@ const DYNAMIC = 'dynamic';
2323

2424
/// Sort by relevance first, highest to lowest, and then by the completion
2525
/// alphabetically.
26-
Comparator<CompletionSuggestion> completionComparator = (a, b) {
26+
Comparator<CompletionSuggestionBuilder> completionComparator = (a, b) {
2727
if (a.relevance == b.relevance) {
2828
return a.completion.compareTo(b.completion);
2929
}

pkg/analysis_server/tool/code_completion/completion_metrics.dart

Lines changed: 93 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,7 @@ class CompletionMetrics {
523523
void recordShadowedCompletion(
524524
String? completionLocation,
525525
ExpectedCompletion expectedCompletion,
526-
protocol.CompletionSuggestion closeMatchSuggestion) {
526+
CompletionSuggestionLite closeMatchSuggestion) {
527527
shadowedCompletions
528528
.putIfAbsent(completionLocation ?? 'unknown', () => [])
529529
.add(ShadowedCompletion(expectedCompletion, closeMatchSuggestion));
@@ -863,7 +863,7 @@ class CompletionQualityMetricsComputer extends CompletionMetricsComputer {
863863
MetricsSuggestionListener listener,
864864
ExpectedCompletion expectedCompletion,
865865
String? completionLocation,
866-
List<protocol.CompletionSuggestion> suggestions,
866+
List<CompletionSuggestionLite> suggestions,
867867
CompletionMetrics metrics,
868868
int elapsedMS) {
869869
var place = placementInSuggestionList(suggestions, expectedCompletion);
@@ -925,7 +925,7 @@ class CompletionQualityMetricsComputer extends CompletionMetricsComputer {
925925

926926
if (options.printMissedCompletionDetails ||
927927
options.printShadowedCompletionDetails) {
928-
protocol.CompletionSuggestion? closeMatchSuggestion;
928+
CompletionSuggestionLite? closeMatchSuggestion;
929929
for (var suggestion in suggestions) {
930930
if (suggestion.completion == expectedCompletion.completion) {
931931
closeMatchSuggestion = suggestion;
@@ -1376,8 +1376,8 @@ class CompletionQualityMetricsComputer extends CompletionMetricsComputer {
13761376
@override
13771377
void setupForResolution(AnalysisContext context) {}
13781378

1379-
int _computeCharsBeforeTop(ExpectedCompletion target,
1380-
List<protocol.CompletionSuggestion> suggestions,
1379+
int _computeCharsBeforeTop(
1380+
ExpectedCompletion target, List<CompletionSuggestionLite> suggestions,
13811381
{int minRank = 1}) {
13821382
var rank = placementInSuggestionList(suggestions, target).rank;
13831383
if (rank <= minRank) {
@@ -1397,15 +1397,13 @@ class CompletionQualityMetricsComputer extends CompletionMetricsComputer {
13971397

13981398
/// Computes completion suggestions for [dartRequest], and returns the
13991399
/// suggestions, sorted by rank and then by completion text.
1400-
Future<List<protocol.CompletionSuggestion>> _computeCompletionSuggestions(
1400+
Future<List<CompletionSuggestionLite>> _computeCompletionSuggestions(
14011401
MetricsSuggestionListener listener,
14021402
OperationPerformanceImpl performance,
14031403
DartCompletionRequest dartRequest,
14041404
NotImportedSuggestions notImportedSuggestions) async {
1405-
List<protocol.CompletionSuggestion> suggestions;
1406-
14071405
var budget = CompletionBudget(Duration(seconds: 30));
1408-
var serverSuggestions = await DartCompletionManager(
1406+
var suggestions = await DartCompletionManager(
14091407
budget: budget,
14101408
listener: listener,
14111409
notImportedSuggestions: notImportedSuggestions,
@@ -1414,19 +1412,16 @@ class CompletionQualityMetricsComputer extends CompletionMetricsComputer {
14141412
performance,
14151413
useFilter: true,
14161414
);
1417-
suggestions = serverSuggestions.map((serverSuggestion) {
1418-
return serverSuggestion.build();
1419-
}).toList();
14201415

14211416
// Note that some routes sort suggestions before responding differently.
14221417
// The Cider and legacy handlers use [fuzzyFilterSort], which does not match
14231418
// [completionComparator].
14241419
suggestions.sort(completionComparator);
1425-
return suggestions;
1420+
return suggestions.map(CompletionSuggestionLite.fromBuilder).toList();
14261421
}
14271422

1428-
List<protocol.CompletionSuggestion> _filterSuggestions(
1429-
String prefix, List<protocol.CompletionSuggestion> suggestions) {
1423+
List<CompletionSuggestionLite> _filterSuggestions(
1424+
String prefix, List<CompletionSuggestionLite> suggestions) {
14301425
// TODO(brianwilkerson): Replace this with a more realistic filtering
14311426
// algorithm.
14321427
return suggestions
@@ -1521,7 +1516,7 @@ class CompletionQualityMetricsComputer extends CompletionMetricsComputer {
15211516
///
15221517
/// If [expectedCompletion] is not found, `Place.none()` is returned.
15231518
static Place placementInSuggestionList(
1524-
List<protocol.CompletionSuggestion> suggestions,
1519+
List<CompletionSuggestionLite> suggestions,
15251520
ExpectedCompletion expectedCompletion) {
15261521
for (var i = 0; i < suggestions.length; i++) {
15271522
if (expectedCompletion.matches(suggestions[i])) {
@@ -1705,6 +1700,78 @@ class CompletionResult {
17051700
}
17061701
}
17071702

1703+
/// Enough data from [CompletionSuggestionBuilder] to compute metrics.
1704+
///
1705+
/// It excludes expensive parts that are not necessary, such as element
1706+
/// properties.
1707+
class CompletionSuggestionLite {
1708+
final String completion;
1709+
final protocol.ElementKind? elementKind;
1710+
final int relevance;
1711+
final protocol.CompletionSuggestionKind kind;
1712+
1713+
CompletionSuggestionLite({
1714+
required this.completion,
1715+
required this.elementKind,
1716+
required this.relevance,
1717+
required this.kind,
1718+
});
1719+
1720+
factory CompletionSuggestionLite.fromBuilder(
1721+
CompletionSuggestionBuilder builder,
1722+
) {
1723+
return CompletionSuggestionLite(
1724+
completion: builder.completion,
1725+
elementKind: builder.elementKind,
1726+
relevance: builder.relevance,
1727+
kind: builder.kind,
1728+
);
1729+
}
1730+
1731+
factory CompletionSuggestionLite.fromJson(Map<String, Object?> map) {
1732+
var elementKindStr = map['elementKind'] as String?;
1733+
1734+
return CompletionSuggestionLite(
1735+
completion: map['completion'] as String,
1736+
elementKind: elementKindStr != null
1737+
? protocol.ElementKind.fromJson(
1738+
ResponseDecoder(null),
1739+
'',
1740+
elementKindStr,
1741+
)
1742+
: null,
1743+
relevance: map['relevance'] as int,
1744+
kind: protocol.CompletionSuggestionKind.fromJson(
1745+
ResponseDecoder(null),
1746+
'',
1747+
map['kind'] as String,
1748+
),
1749+
);
1750+
}
1751+
1752+
@override
1753+
int get hashCode => Object.hash(completion, kind);
1754+
1755+
@override
1756+
bool operator ==(Object other) {
1757+
return other is CompletionSuggestionLite &&
1758+
other.completion == completion &&
1759+
other.kind == kind &&
1760+
other.elementKind == elementKind &&
1761+
other.relevance == relevance;
1762+
}
1763+
1764+
/// Return a map used to represent this suggestion data in a JSON structure.
1765+
Map<String, Object?> toJson() {
1766+
return {
1767+
'completion': completion,
1768+
if (elementKind case var elementKind?) 'elementKind': elementKind.name,
1769+
'relevance': relevance,
1770+
'kind': kind.name,
1771+
};
1772+
}
1773+
}
1774+
17081775
/// The data to be printed on a single line in the table of mrr values per
17091776
/// completion location.
17101777
class LocationTableLine {
@@ -1738,7 +1805,7 @@ class MetricsSuggestionListener implements SuggestionListener {
17381805
0.0
17391806
];
17401807

1741-
Map<protocol.CompletionSuggestion, List<double>> featureMap = Map.identity();
1808+
Map<CompletionSuggestionLite, List<double>> featureMap = Map.identity();
17421809

17431810
List<double> cachedFeatures = noFeatures;
17441811

@@ -1747,8 +1814,8 @@ class MetricsSuggestionListener implements SuggestionListener {
17471814
String? missingCompletionLocationTable;
17481815

17491816
@override
1750-
void builtSuggestion(CompletionSuggestionBuilder suggestionBuilder) {
1751-
var suggestion = suggestionBuilder.build();
1817+
void builtSuggestion(CompletionSuggestionBuilder builder) {
1818+
var suggestion = CompletionSuggestionLite.fromBuilder(builder);
17521819
featureMap[suggestion] = cachedFeatures;
17531820
cachedFeatures = noFeatures;
17541821
}
@@ -1826,15 +1893,15 @@ class RelevanceTables {
18261893
class ShadowedCompletion {
18271894
final ExpectedCompletion expectedCompletion;
18281895

1829-
final protocol.CompletionSuggestion closeMatchSuggestion;
1896+
final CompletionSuggestionLite closeMatchSuggestion;
18301897

18311898
ShadowedCompletion(this.expectedCompletion, this.closeMatchSuggestion);
18321899
}
18331900

18341901
/// The information being remembered about an individual suggestion.
18351902
class SuggestionData {
18361903
/// The suggestion that was produced.
1837-
protocol.CompletionSuggestion suggestion;
1904+
CompletionSuggestionLite suggestion;
18381905

18391906
/// The values of the features used to compute the suggestion.
18401907
List<double> features;
@@ -1844,9 +1911,11 @@ class SuggestionData {
18441911
/// Return an instance extracted from the decoded JSON [map].
18451912
factory SuggestionData.fromJson(Map<String, dynamic> map) {
18461913
return SuggestionData(
1847-
protocol.CompletionSuggestion.fromJson(ResponseDecoder(null), '',
1848-
map['suggestion'] as Map<String, dynamic>),
1849-
(map['features'] as List<dynamic>).cast<double>());
1914+
CompletionSuggestionLite.fromJson(
1915+
map['suggestion'] as Map<String, Object?>,
1916+
),
1917+
(map['features'] as List<dynamic>).cast<double>(),
1918+
);
18501919
}
18511920

18521921
/// Return a map used to represent this suggestion data in a JSON structure.

pkg/analysis_server/tool/code_completion/visitors.dart

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import 'package:analyzer/dart/element/element.dart' as element;
1313
import 'package:analyzer/dart/element/type.dart';
1414
import 'package:analyzer/source/line_info.dart';
1515

16+
import 'completion_metrics.dart';
17+
1618
class ExpectedCompletion {
1719
final String _filePath;
1820

@@ -88,14 +90,14 @@ class ExpectedCompletion {
8890

8991
SyntacticEntity get syntacticEntity => _entity;
9092

91-
bool matches(protocol.CompletionSuggestion completionSuggestion) {
93+
bool matches(CompletionSuggestionLite completionSuggestion) {
9294
if (completionSuggestion.completion == completion) {
9395
if (kind != null && completionSuggestion.kind != kind) {
9496
return false;
9597
}
9698
if (elementKind != null &&
97-
completionSuggestion.element?.kind != null &&
98-
completionSuggestion.element?.kind != elementKind) {
99+
completionSuggestion.elementKind != null &&
100+
completionSuggestion.elementKind != elementKind) {
99101
return false;
100102
}
101103
return true;

0 commit comments

Comments
 (0)