Reduce LSP completion JSON by removing optional fields set to defaults
This addresses some of the things mentioned in https://github.com/dart-lang/sdk/issues/37163 but doesn't entirely solve the issue (for example it doesn't touch docs yet).
Change-Id: Ib0a094695905120ac5e222dae52165b9a5f9a825
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/104860
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/src/lsp/mapping.dart b/pkg/analysis_server/lib/src/lsp/mapping.dart
index a548530..bafec8c 100644
--- a/pkg/analysis_server/lib/src/lsp/mapping.dart
+++ b/pkg/analysis_server/lib/src/lsp/mapping.dart
@@ -165,13 +165,16 @@
declaration.relevanceTags
.forEach((t) => itemRelevance += (tagBoosts[t] ?? 0));
+ // Because we potentially send thousands of these items, we should minimise
+ // the generated JSON as much as possible - for example using nulls in place
+ // of empty lists/false where possible.
return new lsp.CompletionItem(
label,
completionKind,
getDeclarationCompletionDetail(declaration, completionKind, useDeprecated),
asStringOrMarkupContent(formats, cleanDartdoc(declaration.docComplete)),
- useDeprecated ? declaration.isDeprecated : null,
- false, // preselect
+ useDeprecated && declaration.isDeprecated ? true : null,
+ null, // preselect
// Relevance is a number, highest being best. LSP does text sort so subtract
// from a large number so that a text sort will result in the correct order.
// 555 -> 999455
@@ -180,16 +183,15 @@
(1000000 - itemRelevance).toString(),
null, // filterText uses label if not set
null, // insertText is deprecated, but also uses label if not set
- // We don't have completions that use snippets, so we always return PlainText.
- lsp.InsertTextFormat.PlainText,
+ 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,
),
- [], // additionalTextEdits, used for adding imports, etc.
- [], // commitCharacters
+ null, // additionalTextEdits, used for adding imports, etc.
+ null, // commitCharacters
null, // command
// data, used for completionItem/resolve.
new lsp.CompletionItemResolutionInfo(
@@ -375,7 +377,7 @@
} else if (hasParameterType) {
return '$prefix${suggestion.parameterType}';
} else {
- return prefix;
+ return prefix.isNotEmpty ? prefix : null;
}
}
@@ -414,7 +416,7 @@
} else if (hasReturnType) {
return '$prefix${declaration.returnType}';
} else {
- return prefix;
+ return prefix.isNotEmpty ? prefix : null;
}
}
@@ -555,13 +557,16 @@
: suggestionKindToCompletionItemKind(
supportedCompletionItemKinds, suggestion.kind, label);
+ // Because we potentially send thousands of these items, we should minimise
+ // the generated JSON as much as possible - for example using nulls in place
+ // of empty lists/false where possible.
return new lsp.CompletionItem(
label,
completionKind,
getCompletionDetail(suggestion, completionKind, useDeprecated),
asStringOrMarkupContent(formats, cleanDartdoc(suggestion.docComplete)),
- useDeprecated ? suggestion.isDeprecated : null,
- false, // preselect
+ useDeprecated && suggestion.isDeprecated ? true : null,
+ null, // preselect
// Relevance is a number, highest being best. LSP does text sort so subtract
// from a large number so that a text sort will result in the correct order.
// 555 -> 999455
@@ -570,16 +575,15 @@
(1000000 - suggestion.relevance).toString(),
null, // filterText uses label if not set
null, // insertText is deprecated, but also uses label if not set
- // We don't have completions that use snippets, so we always return PlainText.
- lsp.InsertTextFormat.PlainText,
+ 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),
suggestion.completion,
),
- [], // additionalTextEdits, used for adding imports, etc.
- [], // commitCharacters
+ null, // additionalTextEdits, used for adding imports, etc.
+ null, // commitCharacters
null, // command
null, // data, useful for if using lazy resolve, this comes back to us
);
diff --git a/pkg/analysis_server/test/lsp/completion_test.dart b/pkg/analysis_server/test/lsp/completion_test.dart
index 8a378f4..e9a6b94 100644
--- a/pkg/analysis_server/test/lsp/completion_test.dart
+++ b/pkg/analysis_server/test/lsp/completion_test.dart
@@ -217,7 +217,8 @@
final res = await getCompletion(mainFileUri, positionFromMarker(content));
expect(res.any((c) => c.label == 'abcdefghij'), isTrue);
final item = res.singleWhere((c) => c.label == 'abcdefghij');
- expect(item.insertTextFormat, equals(InsertTextFormat.PlainText));
+ expect(item.insertTextFormat,
+ anyOf(equals(InsertTextFormat.PlainText), isNull));
// ignore: deprecated_member_use_from_same_package
expect(item.insertText, anyOf(equals('abcdefghij'), isNull));
final updated = applyTextEdits(withoutMarkers(content), [item.textEdit]);
@@ -445,7 +446,8 @@
final res = await getCompletion(mainFileUri, positionFromMarker(content));
expect(res.any((c) => c.label == 'abcdefghij'), isTrue);
final item = res.singleWhere((c) => c.label == 'abcdefghij');
- expect(item.insertTextFormat, equals(InsertTextFormat.PlainText));
+ expect(item.insertTextFormat,
+ anyOf(equals(InsertTextFormat.PlainText), isNull));
// ignore: deprecated_member_use_from_same_package
expect(item.insertText, anyOf(equals('abcdefghij'), isNull));
final updated = applyTextEdits(withoutMarkers(content), [item.textEdit]);