From 84ca405b355e89ba2133f4fc17c1a8922dcf992a Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Wed, 4 May 2022 20:58:54 +0000 Subject: [PATCH] Support overlays in completion_metrics_client.dart Change-Id: Idedb38193ed80be0fa10e8e6be3fa04f8f30b100 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/243649 Reviewed-by: Brian Wilkerson Commit-Queue: Samuel Rawlins --- .../code_completion/completion_metrics.dart | 35 ++++---- .../completion_metrics_base.dart | 76 +++++++++-------- .../completion_metrics_client.dart | 83 +++++++++++++++---- 3 files changed, 125 insertions(+), 69 deletions(-) diff --git a/pkg/analysis_server/tool/code_completion/completion_metrics.dart b/pkg/analysis_server/tool/code_completion/completion_metrics.dart index 9f142806f85..0520b3f602e 100644 --- a/pkg/analysis_server/tool/code_completion/completion_metrics.dart +++ b/pkg/analysis_server/tool/code_completion/completion_metrics.dart @@ -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 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); } } diff --git a/pkg/analysis_server/tool/code_completion/completion_metrics_base.dart b/pkg/analysis_server/tool/code_completion/completion_metrics_base.dart index 24cf9c65c8a..34056c40a62 100644 --- a/pkg/analysis_server/tool/code_completion/completion_metrics_base.dart +++ b/pkg/analysis_server/tool/code_completion/completion_metrics_base.dart @@ -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 removeOverlay(String filePath); /// Resolves all analyzed files within [context]. Future> 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'); } } diff --git a/pkg/analysis_server/tool/code_completion/completion_metrics_client.dart b/pkg/analysis_server/tool/code_completion/completion_metrics_client.dart index 2248b30b5f1..ad666a8d9df 100644 --- a/pkg/analysis_server/tool/code_completion/completion_metrics_client.dart +++ b/pkg/analysis_server/tool/code_completion/completion_metrics_client.dart @@ -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 removeOverlay(String filePath) async { + if (options.overlay != OverlayMode.none) { + await client.removeOverlay(filePath); + } } @override @@ -275,19 +291,55 @@ class _AnalysisServerClient { Future get onExit => _process!.exitCode; + Future 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; + + return AnalysisUpdateContentResult.fromJson( + ResponseDecoder(null), + 'result', + result, + ); + } + Future dispose() async { return _process?.kill() ?? true; } + Future removeOverlay(String file) async { + final response = await _sendCommand( + 'analysis.updateContent', + params: { + 'files': { + file: {'type': 'remove'}, + } + }, + ); + final result = response['result'] as Map; + + 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: { - '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; 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? ?? - // {}); - response); + _requestCompleters.remove(id)?.complete(response); } } }