Reduce JSON for LSP completions by moving edits to resolve
See https://github.com/dart-lang/sdk/issues/37163.
Change-Id: I336245590560f7b30315a1dbda59ea56e5e9e043
Bug: https://github.com/dart-lang/sdk/issues/37163
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/104862
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Danny Tuppeny <dantup@google.com>
diff --git a/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart b/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart
index 8f4be64..deb8901 100644
--- a/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart
+++ b/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart
@@ -132,43 +132,56 @@
CompletionItemResolutionInfo.canParse,
CompletionItemResolutionInfo.fromJson);
- CompletionItemResolutionInfo(
- this.file, this.offset, this.libraryId, this.autoImportDisplayUri) {
+ CompletionItemResolutionInfo(this.file, this.offset, this.libId,
+ this.displayUri, this.rOffset, this.rLength) {
if (file == null) {
throw 'file is required but was not provided';
}
if (offset == null) {
throw 'offset is required but was not provided';
}
- if (libraryId == null) {
- throw 'libraryId is required but was not provided';
+ if (libId == null) {
+ throw 'libId is required but was not provided';
}
- if (autoImportDisplayUri == null) {
- throw 'autoImportDisplayUri is required but was not provided';
+ if (displayUri == null) {
+ throw 'displayUri is required but was not provided';
+ }
+ if (rOffset == null) {
+ throw 'rOffset is required but was not provided';
+ }
+ if (rLength == null) {
+ throw 'rLength is required but was not provided';
}
}
static CompletionItemResolutionInfo fromJson(Map<String, dynamic> json) {
final file = json['file'];
final offset = json['offset'];
- final libraryId = json['libraryId'];
- final autoImportDisplayUri = json['autoImportDisplayUri'];
+ final libId = json['libId'];
+ final displayUri = json['displayUri'];
+ final rOffset = json['rOffset'];
+ final rLength = json['rLength'];
return new CompletionItemResolutionInfo(
- file, offset, libraryId, autoImportDisplayUri);
+ file, offset, libId, displayUri, rOffset, rLength);
}
- final String autoImportDisplayUri;
+ final String displayUri;
final String file;
- final num libraryId;
+ final num libId;
final num offset;
+ final num rLength;
+ final num rOffset;
Map<String, dynamic> toJson() {
Map<String, dynamic> __result = {};
__result['file'] = file ?? (throw 'file is required but was not set');
__result['offset'] = offset ?? (throw 'offset is required but was not set');
- __result['libraryId'] =
- libraryId ?? (throw 'libraryId is required but was not set');
- __result['autoImportDisplayUri'] = autoImportDisplayUri ??
- (throw 'autoImportDisplayUri is required but was not set');
+ __result['libId'] = libId ?? (throw 'libId is required but was not set');
+ __result['displayUri'] =
+ displayUri ?? (throw 'displayUri is required but was not set');
+ __result['rOffset'] =
+ rOffset ?? (throw 'rOffset is required but was not set');
+ __result['rLength'] =
+ rLength ?? (throw 'rLength is required but was not set');
return __result;
}
@@ -178,10 +191,14 @@
obj['file'] is String &&
obj.containsKey('offset') &&
obj['offset'] is num &&
- obj.containsKey('libraryId') &&
- obj['libraryId'] is num &&
- obj.containsKey('autoImportDisplayUri') &&
- obj['autoImportDisplayUri'] is String;
+ obj.containsKey('libId') &&
+ obj['libId'] is num &&
+ obj.containsKey('displayUri') &&
+ obj['displayUri'] is String &&
+ obj.containsKey('rOffset') &&
+ obj['rOffset'] is num &&
+ obj.containsKey('rLength') &&
+ obj['rLength'] is num;
}
@override
@@ -189,8 +206,10 @@
if (other is CompletionItemResolutionInfo) {
return file == other.file &&
offset == other.offset &&
- libraryId == other.libraryId &&
- autoImportDisplayUri == other.autoImportDisplayUri &&
+ libId == other.libId &&
+ displayUri == other.displayUri &&
+ rOffset == other.rOffset &&
+ rLength == other.rLength &&
true;
}
return false;
@@ -201,8 +220,10 @@
int hash = 0;
hash = JenkinsSmiHash.combine(hash, file.hashCode);
hash = JenkinsSmiHash.combine(hash, offset.hashCode);
- hash = JenkinsSmiHash.combine(hash, libraryId.hashCode);
- hash = JenkinsSmiHash.combine(hash, autoImportDisplayUri.hashCode);
+ hash = JenkinsSmiHash.combine(hash, libId.hashCode);
+ hash = JenkinsSmiHash.combine(hash, displayUri.hashCode);
+ hash = JenkinsSmiHash.combine(hash, rOffset.hashCode);
+ hash = JenkinsSmiHash.combine(hash, rLength.hashCode);
return JenkinsSmiHash.finish(hash);
}
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart
index c519374..aa54dae 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart
@@ -51,12 +51,12 @@
// extracting (with support for the different types of responses between
// the servers). Where is an appropriate place to put it?
- var library = server.declarationsTracker.getLibrary(data.libraryId);
+ var library = server.declarationsTracker.getLibrary(data.libId);
if (library == null) {
return error(
ErrorCodes.InvalidParams,
- 'Library ID is not valid: ${data.libraryId}',
- data.libraryId.toString(),
+ 'Library ID is not valid: ${data.libId}',
+ data.libId.toString(),
);
}
@@ -157,8 +157,8 @@
return success(CompletionItem(
newLabel,
item.kind,
- data.autoImportDisplayUri != null
- ? "Auto import from '${data.autoImportDisplayUri}'\n\n${item.detail ?? ''}"
+ data.displayUri != null
+ ? "Auto import from '${data.displayUri}'\n\n${item.detail ?? ''}"
.trim()
: item.detail,
documentation,
@@ -168,7 +168,12 @@
item.filterText,
newLabel,
item.insertTextFormat,
- item.textEdit,
+ new TextEdit(
+ // TODO(dantup): If `clientSupportsSnippets == true` then we should map
+ // `selection` in to a snippet (see how Dart Code does this).
+ toRange(lineInfo, item.data.rOffset, item.data.rLength),
+ newLabel,
+ ),
thisFilesChanges
.expand((change) =>
change.edits.map((edit) => toTextEdit(lineInfo, edit)))
diff --git a/pkg/analysis_server/lib/src/lsp/mapping.dart b/pkg/analysis_server/lib/src/lsp/mapping.dart
index 28326d5..11bae89 100644
--- a/pkg/analysis_server/lib/src/lsp/mapping.dart
+++ b/pkg/analysis_server/lib/src/lsp/mapping.dart
@@ -183,22 +183,18 @@
null, // filterText uses label if not set
null, // insertText is deprecated, but also uses label if not set
null, // insertTextFormat (we always use plain text so can ommit this)
- new lsp.TextEdit(
- // TODO(dantup): If `clientSupportsSnippets == true` then we should map
- // `selection` in to a snippet (see how Dart Code does this).
- toRange(lineInfo, replacementOffset, replacementLength),
- label,
- ),
+ null, // textEdit - added on during resolve
null, // additionalTextEdits, used for adding imports, etc.
null, // commitCharacters
null, // command
// data, used for completionItem/resolve.
new lsp.CompletionItemResolutionInfo(
- file,
- offset,
- includedSuggestionSet.id,
- includedSuggestionSet.displayUri ?? library.uri?.toString(),
- ),
+ file,
+ offset,
+ includedSuggestionSet.id,
+ includedSuggestionSet.displayUri ?? library.uri?.toString(),
+ replacementOffset,
+ replacementLength),
);
}
diff --git a/pkg/analysis_server/test/lsp/completion_test.dart b/pkg/analysis_server/test/lsp/completion_test.dart
index c2721c0..4e7b84b 100644
--- a/pkg/analysis_server/test/lsp/completion_test.dart
+++ b/pkg/analysis_server/test/lsp/completion_test.dart
@@ -252,8 +252,9 @@
final completion = res.singleWhere((c) => c.label == 'InOtherFile');
expect(completion, isNotNull);
- // Expect no docs, since these are added during resolve.
+ // Expect no docs or text edit, since these are added during resolve.
expect(completion.documentation, isNull);
+ expect(completion.textEdit, isNull);
// Resolve the completion item (via server) to get its edits. This is the
// LSP's equiv of getSuggestionDetails() and is invoked by LSP clients to
@@ -272,6 +273,9 @@
isTrue,
);
+ // Ensure the edit was added on.
+ expect(resolved.textEdit, isNotNull);
+
// There should be no command for this item because it doesn't need imports
// in other files. Same-file completions are in additionalEdits.
expect(resolved.command, isNull);
diff --git a/pkg/analysis_server/tool/lsp_spec/generate_all.dart b/pkg/analysis_server/tool/lsp_spec/generate_all.dart
index 70d21840..08a111d 100644
--- a/pkg/analysis_server/tool/lsp_spec/generate_all.dart
+++ b/pkg/analysis_server/tool/lsp_spec/generate_all.dart
@@ -109,8 +109,10 @@
[
field('file', type: 'string'),
field('offset', type: 'number'),
- field('libraryId', type: 'number'),
- field('autoImportDisplayUri', type: 'string')
+ field('libId', type: 'number'),
+ field('displayUri', type: 'string'),
+ field('rOffset', type: 'number'),
+ field('rLength', type: 'number')
],
),
];