@@ -31,13 +31,14 @@ import 'package:analyzer_plugin/protocol/protocol_common.dart';
3131import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
3232import 'package:analyzer_plugin/src/utilities/completion/completion_target.dart' ;
3333
34- class CompletionHandler
35- extends MessageHandler <CompletionParams , List <CompletionItem >>
34+ class CompletionHandler extends MessageHandler <CompletionParams , CompletionList >
3635 with LspPluginRequestHandlerMixin {
36+ /// Whether to include symbols from libraries that have not been imported.
3737 final bool suggestFromUnimportedLibraries;
38- CompletionHandler (
39- LspAnalysisServer server, this .suggestFromUnimportedLibraries)
40- : super (server);
38+
39+ CompletionHandler (LspAnalysisServer server, LspInitializationOptions options)
40+ : suggestFromUnimportedLibraries = options.suggestFromUnimportedLibraries,
41+ super (server);
4142
4243 @override
4344 Method get handlesMessage => Method .textDocument_completion;
@@ -47,7 +48,7 @@ class CompletionHandler
4748 CompletionParams .jsonHandler;
4849
4950 @override
50- Future <ErrorOr <List < CompletionItem > >> handle (
51+ Future <ErrorOr <CompletionList >> handle (
5152 CompletionParams params, CancellationToken token) async {
5253 final clientCapabilities = server.clientCapabilities;
5354 if (clientCapabilities == null ) {
@@ -56,9 +57,6 @@ class CompletionHandler
5657 'Requests not before server is initilized' );
5758 }
5859
59- final includeSuggestionSets =
60- suggestFromUnimportedLibraries && clientCapabilities.applyEdit;
61-
6260 final triggerCharacter = params.context? .triggerCharacter;
6361 final pos = params.position;
6462 final path = pathOfDoc (params.textDocument);
@@ -75,14 +73,13 @@ class CompletionHandler
7573 await lineInfo.mapResult ((lineInfo) => toOffset (lineInfo, pos));
7674
7775 return offset.mapResult ((offset) async {
78- Future <ErrorOr <List < CompletionItem > >>? serverResultsFuture;
76+ Future <ErrorOr <CompletionList >>? serverResultsFuture;
7977 final pathContext = server.resourceProvider.pathContext;
8078 final fileExtension = pathContext.extension (path.result);
8179
8280 if (fileExtension == '.dart' && ! unit.isError) {
8381 serverResultsFuture = _getServerDartItems (
8482 clientCapabilities,
85- includeSuggestionSets,
8683 unit.result,
8784 offset,
8885 triggerCharacter,
@@ -110,7 +107,8 @@ class CompletionHandler
110107 }
111108 }
112109
113- serverResultsFuture ?? = Future .value (success (const < CompletionItem > []));
110+ serverResultsFuture ?? =
111+ Future .value (success (CompletionList (isIncomplete: false , items: [])));
114112
115113 final pluginResultsFuture = _getPluginResults (
116114 clientCapabilities, lineInfo.result, path.result, offset);
@@ -125,9 +123,15 @@ class CompletionHandler
125123 if (serverResults.isError) return serverResults;
126124 if (pluginResults.isError) return pluginResults;
127125
128- return success (
129- serverResults.result.followedBy (pluginResults.result).toList (),
130- );
126+ return success (CompletionList (
127+ // If any set of the results is incomplete, the whole batch must be
128+ // marked as such.
129+ isIncomplete: serverResults.result.isIncomplete ||
130+ pluginResults.result.isIncomplete,
131+ items: serverResults.result.items
132+ .followedBy (pluginResults.result.items)
133+ .toList (),
134+ ));
131135 });
132136 }
133137
@@ -174,7 +178,7 @@ class CompletionHandler
174178 String _createImportedSymbolKey (String name, Uri declaringUri) =>
175179 '$name /$declaringUri ' ;
176180
177- Future <ErrorOr <List < CompletionItem > >> _getPluginResults (
181+ Future <ErrorOr <CompletionList >> _getPluginResults (
178182 LspClientCapabilities capabilities,
179183 LineInfo lineInfo,
180184 String path,
@@ -188,22 +192,27 @@ class CompletionHandler
188192 .map ((e) => plugin.CompletionGetSuggestionsResult .fromResponse (e))
189193 .toList ();
190194
191- return success (_pluginResultsToItems (
192- capabilities,
193- lineInfo,
194- offset,
195- pluginResults,
196- ).toList ());
195+ return success (CompletionList (
196+ isIncomplete: false ,
197+ items: _pluginResultsToItems (
198+ capabilities,
199+ lineInfo,
200+ offset,
201+ pluginResults,
202+ ).toList (),
203+ ));
197204 }
198205
199- Future <ErrorOr <List < CompletionItem > >> _getServerDartItems (
206+ Future <ErrorOr <CompletionList >> _getServerDartItems (
200207 LspClientCapabilities capabilities,
201- bool includeSuggestionSets,
202208 ResolvedUnitResult unit,
203209 int offset,
204210 String ? triggerCharacter,
205211 CancellationToken token,
206212 ) async {
213+ final useSuggestionSets =
214+ suggestFromUnimportedLibraries && capabilities.applyEdit;
215+
207216 var performance = OperationPerformanceImpl ('<root>' );
208217 return await performance.runAsync (
209218 'request' ,
@@ -226,14 +235,14 @@ class CompletionHandler
226235
227236 if (triggerCharacter != null ) {
228237 if (! _triggerCharacterValid (offset, triggerCharacter, target)) {
229- return success ([] );
238+ return success (CompletionList (isIncomplete : false , items : []) );
230239 }
231240 }
232241
233242 Set <ElementKind >? includedElementKinds;
234243 Set <String >? includedElementNames;
235244 List <IncludedSuggestionRelevanceTag >? includedSuggestionRelevanceTags;
236- if (includeSuggestionSets ) {
245+ if (useSuggestionSets ) {
237246 includedElementKinds = < ElementKind > {};
238247 includedElementNames = < String > {};
239248 includedSuggestionRelevanceTags = < IncludedSuggestionRelevanceTag > [];
@@ -284,18 +293,22 @@ class CompletionHandler
284293 offset, itemReplacementOffset, itemInsertLength);
285294 }
286295
296+ // Convert to LSP ranges using the LineInfo.
297+ Range ? replacementRange = toRange (
298+ unit.lineInfo, itemReplacementOffset, itemReplacementLength);
299+ Range ? insertionRange = toRange (
300+ unit.lineInfo, itemReplacementOffset, itemInsertLength);
301+
287302 return toCompletionItem (
288303 capabilities,
289304 unit.lineInfo,
290305 item,
291- itemReplacementOffset,
292- itemInsertLength,
293- itemReplacementLength,
294- // TODO(dantup): Including commit characters in every completion
295- // increases the payload size. The LSP spec is ambigious
296- // about how this should be handled (and VS Code requires it) but
297- // this should be removed (or made conditional based on a capability)
298- // depending on how the spec is updated.
306+ replacementRange: replacementRange,
307+ insertionRange: insertionRange,
308+ // TODO(dantup): Move commit characters to the main response
309+ // and remove from each individual item (to reduce payload size)
310+ // once the following change ships (and the Dart VS Code
311+ // extension is updated to use it).
299312 // https://github.com/microsoft/vscode-languageserver-node/issues/673
300313 includeCommitCharacters:
301314 server.clientConfiguration.global.previewCommitCharacters,
@@ -390,11 +403,10 @@ class CompletionHandler
390403 completionRequest.replacementOffset,
391404 insertLength,
392405 completionRequest.replacementLength,
393- // TODO(dantup): Including commit characters in every completion
394- // increases the payload size. The LSP spec is ambigious
395- // about how this should be handled (and VS Code requires it) but
396- // this should be removed (or made conditional based on a capability)
397- // depending on how the spec is updated.
406+ // TODO(dantup): Move commit characters to the main response
407+ // and remove from each individual item (to reduce payload size)
408+ // once the following change ships (and the Dart VS Code
409+ // extension is updated to use it).
398410 // https://github.com/microsoft/vscode-languageserver-node/issues/673
399411 includeCommitCharacters: server
400412 .clientConfiguration.global.previewCommitCharacters,
@@ -415,15 +427,16 @@ class CompletionHandler
415427
416428 completionPerformance.suggestionCount = results.length;
417429
418- return success (matchingResults);
430+ return success (
431+ CompletionList (isIncomplete: false , items: matchingResults));
419432 } on AbortCompletion {
420- return success ([] );
433+ return success (CompletionList (isIncomplete : false , items : []) );
421434 }
422435 },
423436 );
424437 }
425438
426- Future <ErrorOr <List < CompletionItem > >> _getServerYamlItems (
439+ Future <ErrorOr <CompletionList >> _getServerYamlItems (
427440 YamlCompletionGenerator generator,
428441 LspClientCapabilities capabilities,
429442 String path,
@@ -437,23 +450,25 @@ class CompletionHandler
437450 suggestions.replacementOffset,
438451 suggestions.replacementLength,
439452 );
453+ final replacementRange = toRange (
454+ lineInfo, suggestions.replacementOffset, suggestions.replacementLength);
455+ final insertionRange =
456+ toRange (lineInfo, suggestions.replacementOffset, insertLength);
457+
440458 final completionItems = suggestions.suggestions
441459 .map (
442460 (item) => toCompletionItem (
443461 capabilities,
444462 lineInfo,
445463 item,
446- suggestions.replacementOffset,
447- insertLength,
448- suggestions.replacementLength,
464+ replacementRange: replacementRange,
465+ insertionRange: insertionRange,
449466 includeCommitCharacters: false ,
450467 completeFunctionCalls: false ,
451468 // Add on any completion-kind-specific resolution data that will be
452469 // used during resolve() calls to provide additional information.
453470 resolutionData: item.kind == CompletionSuggestionKind .PACKAGE_NAME
454471 ? PubPackageCompletionItemResolutionInfo (
455- file: path,
456- offset: offset,
457472 // The completion for package names may contain a trailing
458473 // ': ' for convenience, so if it's there, trim it off.
459474 packageName: item.completion.split (':' ).first,
@@ -462,7 +477,7 @@ class CompletionHandler
462477 ),
463478 )
464479 .toList ();
465- return success (completionItems);
480+ return success (CompletionList (isIncomplete : false , items : completionItems) );
466481 }
467482
468483 /// Returns true if [node] is part of an invocation and already has an argument
@@ -495,18 +510,23 @@ class CompletionHandler
495510 List <plugin.CompletionGetSuggestionsResult > pluginResults,
496511 ) {
497512 return pluginResults.expand ((result) {
513+ final insertLength = _computeInsertLength (
514+ offset,
515+ result.replacementOffset,
516+ result.replacementLength,
517+ );
518+ final replacementRange =
519+ toRange (lineInfo, result.replacementOffset, result.replacementLength);
520+ final insertionRange =
521+ toRange (lineInfo, result.replacementOffset, insertLength);
522+
498523 return result.results.map (
499524 (item) => toCompletionItem (
500525 capabilities,
501526 lineInfo,
502527 item,
503- result.replacementOffset,
504- _computeInsertLength (
505- offset,
506- result.replacementOffset,
507- result.replacementLength,
508- ),
509- result.replacementLength,
528+ replacementRange: replacementRange,
529+ insertionRange: insertionRange,
510530 // Plugins cannot currently contribute commit characters and we should
511531 // not assume that the Dart ones would be correct for all of their
512532 // completions.
0 commit comments