Support overlays in completion_metrics_client.dart

Change-Id: Idedb38193ed80be0fa10e8e6be3fa04f8f30b100
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/243649
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Samuel Rawlins <srawlins@google.com>
This commit is contained in:
Sam Rawlins 2022-05-04 20:58:54 +00:00 committed by Commit Bot
parent 78f218eb4c
commit 84ca405b35
3 changed files with 125 additions and 69 deletions

View file

@ -155,11 +155,11 @@ ArgParser createArgParser() {
)
..addOption(CompletionMetricsOptions.OVERLAY,
allowed: [
CompletionMetricsOptions.OVERLAY_NONE,
CompletionMetricsOptions.OVERLAY_REMOVE_TOKEN,
CompletionMetricsOptions.OVERLAY_REMOVE_REST_OF_FILE
OverlayMode.none.flag,
OverlayMode.removeRestOfFile.flag,
OverlayMode.removeToken.flag,
],
defaultsTo: CompletionMetricsOptions.OVERLAY_NONE,
defaultsTo: OverlayMode.none.flag,
help:
'Before attempting a completion at the location of each token, the '
'token can be removed, or the rest of the file can be removed to '
@ -745,17 +745,20 @@ class CompletionQualityMetricsComputer extends CompletionMetricsComputer {
ExpectedCompletion expectedCompletion,
) async {
// If an overlay option is being used, compute the overlay file, and
// have the context reanalyze the file
if (options.overlay != CompletionMetricsOptions.OVERLAY_NONE) {
var overlayContents = CompletionMetricsComputer.getOverlayContents(
resolvedUnitResult.content,
expectedCompletion,
options.overlay,
options.prefixLength);
// have the context reanalyze the file.
if (options.overlay != OverlayMode.none) {
final overlayContents = CompletionMetricsComputer.getOverlayContent(
resolvedUnitResult.content,
expectedCompletion,
options.overlay,
options.prefixLength,
);
provider.setOverlay(filePath,
content: overlayContents,
modificationStamp: overlayModificationStamp++);
provider.setOverlay(
filePath,
content: overlayContents,
modificationStamp: overlayModificationStamp++,
);
context.changeFile(filePath);
await context.applyPendingFileChanges();
resolvedUnitResult = await context.currentSession
@ -1396,10 +1399,10 @@ class CompletionQualityMetricsComputer extends CompletionMetricsComputer {
}
@override
void removeOverlay(String filePath) {
Future<void> removeOverlay(String filePath) async {
// If an overlay option is being used, remove the overlay applied
// earlier.
if (options.overlay != CompletionMetricsOptions.OVERLAY_NONE) {
if (options.overlay != OverlayMode.none) {
provider.removeOverlay(filePath);
}
}

View file

@ -87,7 +87,7 @@ abstract class CompletionMetricsComputer {
documentationCache,
);
removeOverlay(filePath);
await removeOverlay(filePath);
}
progress.tick();
}
@ -113,7 +113,7 @@ abstract class CompletionMetricsComputer {
);
/// Removes the overlay which has been applied to [filePath].
void removeOverlay(String filePath);
Future<void> removeOverlay(String filePath);
/// Resolves all analyzed files within [context].
Future<List<ResolvedUnitResult>> resolveAnalyzedFiles({
@ -171,17 +171,16 @@ abstract class CompletionMetricsComputer {
return null;
}
/// Gets overlay contents for [contents], applying a change at
/// Gets overlay content for [content], applying a change at
/// [expectedCompletion] with [prefixLength], according to [overlay], one of
/// the [CompletionMetricsOptions].
static String getOverlayContents(
String contents,
static String getOverlayContent(
String content,
ExpectedCompletion expectedCompletion,
// TODO(srawlins): Replace this with an enum.
String overlay,
OverlayMode overlay,
int prefixLength,
) {
assert(contents.isNotEmpty);
assert(content.isNotEmpty);
var offset = expectedCompletion.offset;
final length = expectedCompletion.syntacticEntity.length;
assert(offset >= 0);
@ -192,16 +191,12 @@ abstract class CompletionMetricsComputer {
// the given prefix length.
offset += prefixLength;
}
if (overlay == CompletionMetricsOptions.OVERLAY_REMOVE_TOKEN) {
return contents.substring(0, offset) + contents.substring(tokenEndOffset);
} else if (overlay ==
CompletionMetricsOptions.OVERLAY_REMOVE_REST_OF_FILE) {
return contents.substring(0, offset);
if (overlay == OverlayMode.removeToken) {
return content.substring(0, offset) + content.substring(tokenEndOffset);
} else if (overlay == OverlayMode.removeRestOfFile) {
return content.substring(0, offset);
} else {
final removeToken = CompletionMetricsOptions.OVERLAY_REMOVE_TOKEN;
final removeRest = CompletionMetricsOptions.OVERLAY_REMOVE_REST_OF_FILE;
throw Exception('\'getOverlayContents\' called with option other than'
'$removeToken and $removeRest: $overlay');
throw ArgumentError.value(overlay, 'overlay');
}
}
}
@ -211,18 +206,6 @@ class CompletionMetricsOptions {
/// An option to control whether and how overlays should be produced.
static const String OVERLAY = 'overlay';
/// A mode indicating that no overlays should be produced.
/// TODO(srawlins): Replace this and the other two overlay values with enums.
static const String OVERLAY_NONE = 'none';
/// A mode indicating that everything from the completion offset to the end of
/// the file should be removed.
static const String OVERLAY_REMOVE_REST_OF_FILE = 'remove-rest-of-file';
/// A mode indicating that the token whose offset is the same as the
/// completion offset should be removed.
static const String OVERLAY_REMOVE_TOKEN = 'remove-token';
/// An option controlling how long of a prefix should be used.
///
/// This affects the offset of the completion request, and how much content is
@ -234,8 +217,7 @@ class CompletionMetricsOptions {
static const String PRINT_SLOWEST_RESULTS = 'print-slowest-results';
/// The overlay mode that should be used.
/// TODO(srawlins): Replace this with an enum.
final String overlay;
final OverlayMode overlay;
final int prefixLength;
@ -244,12 +226,34 @@ class CompletionMetricsOptions {
final bool printSlowestResults;
CompletionMetricsOptions(ArgResults results)
: overlay = results[OVERLAY] as String,
: overlay = OverlayMode.parseFlag(results[OVERLAY] as String),
prefixLength = int.parse(results[PREFIX_LENGTH] as String),
printSlowestResults = results[PRINT_SLOWEST_RESULTS] as bool {
assert(overlay == OVERLAY_NONE ||
overlay == OVERLAY_REMOVE_TOKEN ||
overlay == OVERLAY_REMOVE_REST_OF_FILE);
printSlowestResults = results[PRINT_SLOWEST_RESULTS] as bool;
}
enum OverlayMode {
/// A mode indicating that no overlays should be produced.
none('none'),
/// A mode indicating that everything from the completion offset to the end of
/// the file should be removed.
removeRestOfFile('remove-rest-of-file'),
/// A mode indicating that the token whose offset is the same as the
/// completion offset should be removed.
removeToken('remove-token');
final String flag;
const OverlayMode(this.flag);
static OverlayMode parseFlag(String flag) {
for (final mode in values) {
if (flag == mode.flag) {
return mode;
}
}
throw ArgumentError.value(flag, 'overlay');
}
}

View file

@ -68,11 +68,11 @@ ArgParser _createArgParser() {
..addOption(
CompletionMetricsOptions.OVERLAY,
allowed: [
CompletionMetricsOptions.OVERLAY_NONE,
CompletionMetricsOptions.OVERLAY_REMOVE_TOKEN,
CompletionMetricsOptions.OVERLAY_REMOVE_REST_OF_FILE,
OverlayMode.none.flag,
OverlayMode.removeRestOfFile.flag,
OverlayMode.removeToken.flag,
],
defaultsTo: CompletionMetricsOptions.OVERLAY_NONE,
defaultsTo: OverlayMode.none.flag,
help: 'Before attempting a completion at the location of each token, the '
'token can be removed, or the rest of the file can be removed to '
'test code completion with diverse methods. The default mode is to '
@ -143,7 +143,21 @@ class CompletionClientMetricsComputer extends CompletionMetricsComputer {
String filePath,
ExpectedCompletion expectedCompletion,
) async {
// TODO(srawlins): Support overlays.
if (options.overlay != OverlayMode.none) {
final overlayContent = CompletionMetricsComputer.getOverlayContent(
resolvedUnitResult.content,
expectedCompletion,
options.overlay,
options.prefixLength,
);
provider.setOverlay(
filePath,
content: overlayContent,
modificationStamp: overlayModificationStamp++,
);
await client.addOverlay(filePath, overlayContent);
}
}
@override
@ -193,8 +207,10 @@ class CompletionClientMetricsComputer extends CompletionMetricsComputer {
}
@override
void removeOverlay(String filePath) {
// TODO(srawlins): Support overlays.
Future<void> removeOverlay(String filePath) async {
if (options.overlay != OverlayMode.none) {
await client.removeOverlay(filePath);
}
}
@override
@ -275,19 +291,55 @@ class _AnalysisServerClient {
Future<int> get onExit => _process!.exitCode;
Future<AnalysisUpdateContentResult> addOverlay(
String file, String content) async {
final response = await _sendCommand(
'analysis.updateContent',
params: {
'files': {
file: {'type': 'add', 'content': content},
}
},
);
final result = response['result'] as Map<String, dynamic>;
return AnalysisUpdateContentResult.fromJson(
ResponseDecoder(null),
'result',
result,
);
}
Future<bool> dispose() async {
return _process?.kill() ?? true;
}
Future<AnalysisUpdateContentResult> removeOverlay(String file) async {
final response = await _sendCommand(
'analysis.updateContent',
params: {
'files': {
file: {'type': 'remove'},
}
},
);
final result = response['result'] as Map<String, dynamic>;
return AnalysisUpdateContentResult.fromJson(
ResponseDecoder(null),
'result',
result,
);
}
/// Requests a completion for [file] at [offset].
Future<_SuggestionsData> requestCompletion(
String file, int offset, int maxResults) async {
final response = await _sendCommand('completion.getSuggestions2',
params: <String, dynamic>{
'file': file,
'offset': offset,
'maxResults': maxResults,
});
final response = await _sendCommand('completion.getSuggestions2', params: {
'file': file,
'offset': offset,
'maxResults': maxResults,
});
final result = response['result'] as Map<String, dynamic>;
final metadata = _requestMetadata[response['id']]!;
@ -440,10 +492,7 @@ class _AnalysisServerClient {
.remove(id)
?.completeError(_RequestError.parse(error));
} else {
_requestCompleters.remove(id)?.complete(
//response['result'] as Map<String, dynamic>? ??
// <String, dynamic>{});
response);
_requestCompleters.remove(id)?.complete(response);
}
}
}