protected static LSP.VSCompletionItem CreateCompletionItem( string insertText, LSP.CompletionItemKind kind, string[] tags, LSP.CompletionParams requestParameters, bool preselect = false, ImmutableArray <char>?commitCharacters = null, string?sortText = null, int resultId = 0) { var item = new LSP.VSCompletionItem() { FilterText = insertText, InsertText = insertText, Label = insertText, SortText = sortText ?? insertText, InsertTextFormat = LSP.InsertTextFormat.Plaintext, Kind = kind, Data = JObject.FromObject(new CompletionResolveData() { DisplayText = insertText, TextDocument = requestParameters.TextDocument, Position = requestParameters.Position, CompletionTrigger = ProtocolConversions.LSPToRoslynCompletionTrigger(requestParameters.Context), ResultId = resultId, }), Preselect = preselect }; if (tags != null) { item.Icon = tags.ToImmutableArray().GetFirstGlyph().GetImageElement(); } if (commitCharacters != null) { item.CommitCharacters = commitCharacters.Value.Select(c => c.ToString()).ToArray(); } return(item); }
public async Task <LSP.CompletionList?> HandleRequestAsync(LSP.CompletionParams request, RequestContext context, CancellationToken cancellationToken) { var document = context.Document; if (document == null) { return(null); } // C# and VB share the same LSP language server, and thus share the same default trigger characters. // We need to ensure the trigger character is valid in the document's language. For example, the '{' // character, while a trigger character in VB, is not a trigger character in C#. if (request.Context != null && request.Context.TriggerKind == LSP.CompletionTriggerKind.TriggerCharacter && !char.TryParse(request.Context.TriggerCharacter, out var triggerCharacter) && !char.IsLetterOrDigit(triggerCharacter) && !IsValidTriggerCharacterForDocument(document, triggerCharacter)) { return(null); } var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); var completionOptions = await GetCompletionOptionsAsync(document, cancellationToken).ConfigureAwait(false); var completionService = document.Project.LanguageServices.GetRequiredService <CompletionService>(); // TO-DO: More LSP.CompletionTriggerKind mappings are required to properly map to Roslyn CompletionTriggerKinds. // https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1178726 var completionTrigger = ProtocolConversions.LSPToRoslynCompletionTrigger(request.Context); var list = await completionService.GetCompletionsAsync(document, position, completionTrigger, options : completionOptions, cancellationToken : cancellationToken).ConfigureAwait(false); if (list == null) { return(null); } var lspVSClientCapability = context.ClientCapabilities?.HasVisualStudioLspCapability() == true; var commitCharactersRuleCache = new Dictionary <ImmutableArray <CharacterSetModificationRule>, ImmutableArray <string> >(); // Cache the completion list so we can avoid recomputation in the resolve handler var resultId = await _completionListCache.UpdateCacheAsync(list, cancellationToken).ConfigureAwait(false); return(new LSP.VSCompletionList { Items = list.Items.Select(item => CreateLSPCompletionItem( request, item, resultId, lspVSClientCapability, completionTrigger, commitCharactersRuleCache)).ToArray(), SuggestionMode = list.SuggestionModeItem != null, }); // Local functions bool IsValidTriggerCharacterForDocument(Document document, char triggerCharacter) { if (document.Project.Language == LanguageNames.CSharp) { return(_csharpTriggerCharacters.Contains(triggerCharacter)); } else if (document.Project.Language == LanguageNames.VisualBasic) { return(_vbTriggerCharacters.Contains(triggerCharacter)); } // Typescript still calls into this for completion. // Since we don't know what their trigger characters are, just return true. return(true); }
public async Task <LSP.CompletionList?> HandleRequestAsync(LSP.CompletionParams request, RequestContext context, CancellationToken cancellationToken) { var document = context.Document; if (document == null) { return(null); } // C# and VB share the same LSP language server, and thus share the same default trigger characters. // We need to ensure the trigger character is valid in the document's language. For example, the '{' // character, while a trigger character in VB, is not a trigger character in C#. if (request.Context != null && request.Context.TriggerKind == LSP.CompletionTriggerKind.TriggerCharacter && !char.TryParse(request.Context.TriggerCharacter, out var triggerCharacter) && !char.IsLetterOrDigit(triggerCharacter) && !IsValidTriggerCharacterForDocument(document, triggerCharacter)) { return(null); } var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); // Filter out snippets as they are not supported in the LSP client // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1139740 // Filter out unimported types for now as there are two issues with providing them: // 1. LSP client does not currently provide a way to provide detail text on the completion item to show the namespace. // https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1076759 // 2. We need to figure out how to provide the text edits along with the completion item or provide them in the resolve request. // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/985860/ // 3. LSP client should support completion filters / expanders var documentOptions = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); var completionOptions = documentOptions .WithChangedOption(CompletionOptions.SnippetsBehavior, SnippetsRule.NeverInclude) .WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, false) .WithChangedOption(CompletionServiceOptions.IsExpandedCompletion, false) .WithChangedOption(CompletionServiceOptions.DisallowAddingImports, true); var completionService = document.Project.LanguageServices.GetRequiredService <CompletionService>(); // TO-DO: More LSP.CompletionTriggerKind mappings are required to properly map to Roslyn CompletionTriggerKinds. // https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1178726 var completionTrigger = ProtocolConversions.LSPToRoslynCompletionTrigger(request.Context); var list = await completionService.GetCompletionsAsync(document, position, completionTrigger, options : completionOptions, cancellationToken : cancellationToken).ConfigureAwait(false); if (list == null) { return(null); } var lspVSClientCapability = context.ClientCapabilities?.HasVisualStudioLspCapability() == true; var commitCharactersRuleCache = new Dictionary <ImmutableArray <CharacterSetModificationRule>, ImmutableArray <string> >(); return(new LSP.VSCompletionList { Items = list.Items.Select(item => CreateLSPCompletionItem(request, item, lspVSClientCapability, completionTrigger, commitCharactersRuleCache)).ToArray(), SuggestionMode = list.SuggestionModeItem != null, }); // Local functions bool IsValidTriggerCharacterForDocument(Document document, char triggerCharacter) { if (document.Project.Language == LanguageNames.CSharp) { return(_csharpTriggerCharacters.Contains(triggerCharacter)); } else if (document.Project.Language == LanguageNames.VisualBasic) { return(_vbTriggerCharacters.Contains(triggerCharacter)); } // Typescript still calls into this for completion. // Since we don't know what their trigger characters are, just return true. return(true); }