Пример #1
0
        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#.
            var triggerCharacter = char.Parse(request.Context.TriggerCharacter);

            if (request.Context.TriggerKind == LSP.CompletionTriggerKind.TriggerCharacter && !char.IsLetterOrDigit(triggerCharacter) &&
                !IsValidTriggerCharacterForDocument(document, request.Context.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 triggerKind       = ProtocolConversions.LSPToRoslynCompletionTriggerKind(request.Context.TriggerKind);
            var completionTrigger = new CompletionTrigger(triggerKind, triggerCharacter);

            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(),
                SuggesstionMode = list.SuggestionModeItem != null,
            });

            // Local functions
            bool IsValidTriggerCharacterForDocument(Document document, string 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);
            }