File: DefaultCapabilitiesProvider.cs
Web Access
Project: src\src\LanguageServer\Protocol\Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj (Microsoft.CodeAnalysis.LanguageServer.Protocol)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.Completion.Providers;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageServer.Handler.Completion;
using Microsoft.CodeAnalysis.LanguageServer.Handler.SemanticTokens;
using Roslyn.LanguageServer.Protocol;
 
namespace Microsoft.CodeAnalysis.LanguageServer
{
    [Export(typeof(ExperimentalCapabilitiesProvider)), Shared]
    internal sealed class ExperimentalCapabilitiesProvider : ICapabilitiesProvider
    {
        private readonly ImmutableArray<Lazy<CompletionProvider, CompletionProviderMetadata>> _completionProviders;
 
        [ImportingConstructor]
        [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
        public ExperimentalCapabilitiesProvider(
            [ImportMany] IEnumerable<Lazy<CompletionProvider, CompletionProviderMetadata>> completionProviders)
        {
            _completionProviders = completionProviders
                .Where(lz => lz.Metadata.Language is LanguageNames.CSharp or LanguageNames.VisualBasic)
                .ToImmutableArray();
        }
 
        public void Initialize()
        {
            // Force completion providers to resolve in initialize, because it means MEF parts will be loaded.
            // We need to do this before GetCapabilities is called as that is on the UI thread, and loading MEF parts
            // could cause assembly loads, which we want to do off the UI thread.
            foreach (var completionProvider in _completionProviders)
            {
                _ = completionProvider.Value;
            }
        }
 
        public ServerCapabilities GetCapabilities(ClientCapabilities clientCapabilities)
        {
            var supportsVsExtensions = clientCapabilities.HasVisualStudioLspCapability();
            var capabilities = supportsVsExtensions ? GetVSServerCapabilities() : new VSInternalServerCapabilities();
 
            var commitCharacters = CompletionResultFactory.DefaultCommitCharactersArray;
            var triggerCharacters = _completionProviders.SelectMany(
                lz => CommonCompletionUtilities.GetTriggerCharacters(lz.Value)).Distinct().Select(c => c.ToString()).ToArray();
 
            capabilities.DefinitionProvider = true;
            capabilities.TypeDefinitionProvider = true;
            capabilities.DocumentHighlightProvider = true;
            capabilities.RenameProvider = new RenameOptions
            {
                PrepareProvider = true,
            };
            capabilities.ImplementationProvider = true;
            capabilities.CodeActionProvider = new CodeActionOptions { CodeActionKinds = [CodeActionKind.QuickFix, CodeActionKind.Refactor], ResolveProvider = true };
            capabilities.CompletionProvider = new Roslyn.LanguageServer.Protocol.CompletionOptions
            {
                ResolveProvider = true,
                AllCommitCharacters = commitCharacters,
                TriggerCharacters = triggerCharacters,
            };
 
            capabilities.SignatureHelpProvider = new SignatureHelpOptions { TriggerCharacters = ["(", ","] };
            capabilities.DocumentSymbolProvider = true;
            capabilities.WorkspaceSymbolProvider = true;
            capabilities.DocumentFormattingProvider = true;
            capabilities.DocumentRangeFormattingProvider = true;
            capabilities.DocumentOnTypeFormattingProvider = new DocumentOnTypeFormattingOptions { FirstTriggerCharacter = "}", MoreTriggerCharacter = [";", "\n"] };
            capabilities.ReferencesProvider = new ReferenceOptions
            {
                WorkDoneProgress = true,
            };
 
            capabilities.FoldingRangeProvider = true;
            capabilities.ExecuteCommandProvider = new ExecuteCommandOptions() { Commands = [] };
            capabilities.TextDocumentSync = new TextDocumentSyncOptions
            {
                Change = TextDocumentSyncKind.Incremental,
                OpenClose = true
            };
 
            capabilities.HoverProvider = true;
 
            // Using only range handling has shown to be more performant than using a combination of full/edits/range
            // handling, especially for larger files. With range handling, we only need to compute tokens for whatever
            // is in view, while with full/edits handling we need to compute tokens for the entire file and then
            // potentially run a diff between the old and new tokens.
            capabilities.SemanticTokensOptions = new SemanticTokensOptions
            {
                Full = false,
                Range = true,
                Legend = new SemanticTokensLegend
                {
                    TokenTypes = [.. SemanticTokensSchema.GetSchema(clientCapabilities.HasVisualStudioLspCapability()).AllTokenTypes],
                    TokenModifiers = SemanticTokensSchema.TokenModifiers
                }
            };
 
            capabilities.CodeLensProvider = new CodeLensOptions
            {
                ResolveProvider = true,
                // TODO - Code lens should support streaming
                // See https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1730465
                WorkDoneProgress = false,
            };
 
            capabilities.InlayHintOptions = new InlayHintOptions
            {
                ResolveProvider = true,
                WorkDoneProgress = false,
            };
 
            // Using VS server capabilities because we have our own custom client.
            capabilities.OnAutoInsertProvider = new VSInternalDocumentOnAutoInsertOptions { TriggerCharacters = ["'", "/", "\n"] };
 
            return capabilities;
        }
 
        private static VSInternalServerCapabilities GetVSServerCapabilities()
            => new()
            {
                ProjectContextProvider = true,
                BreakableRangeProvider = true,
 
                // Diagnostic requests are only supported from PullDiagnosticsInProcLanguageClient.
                SupportsDiagnosticRequests = false,
            };
    }
}