File: Completion\Providers\AbstractPartialMethodCompletionProvider.cs
Web Access
Project: src\src\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj (Microsoft.CodeAnalysis.Features)
// 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.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
 
namespace Microsoft.CodeAnalysis.Completion.Providers;
 
internal abstract partial class AbstractPartialMethodCompletionProvider : AbstractMemberInsertingCompletionProvider
{
    protected static readonly SymbolDisplayFormat SignatureDisplayFormat =
            new(
                genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
                memberOptions:
                    SymbolDisplayMemberOptions.IncludeParameters,
                parameterOptions:
                    SymbolDisplayParameterOptions.IncludeName |
                    SymbolDisplayParameterOptions.IncludeType |
                    SymbolDisplayParameterOptions.IncludeParamsRefOut,
                miscellaneousOptions:
                    SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers |
                    SymbolDisplayMiscellaneousOptions.UseSpecialTypes);
 
    protected AbstractPartialMethodCompletionProvider()
    {
    }
 
    protected abstract bool IncludeAccessibility(IMethodSymbol method, CancellationToken cancellationToken);
    protected abstract bool IsPartialMethodCompletionContext(SyntaxTree tree, int position, CancellationToken cancellationToken, out DeclarationModifiers modifiers, out SyntaxToken token);
    protected abstract string GetDisplayText(IMethodSymbol method, SemanticModel semanticModel, int position);
    protected abstract bool IsPartial(IMethodSymbol method);
 
    public override async Task ProvideCompletionsAsync(CompletionContext context)
    {
        var document = context.Document;
        var position = context.Position;
        var cancellationToken = context.CancellationToken;
 
        var tree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
        if (!IsPartialMethodCompletionContext(tree, position, cancellationToken, out var modifiers, out var token))
        {
            return;
        }
 
        var items = await CreatePartialItemsAsync(
            document, position, context.CompletionListSpan, modifiers, token, cancellationToken).ConfigureAwait(false);
 
        if (items?.Any() == true)
        {
            context.IsExclusive = true;
            context.AddItems(items);
        }
    }
 
    protected override async Task<ISymbol> GenerateMemberAsync(ISymbol member, INamedTypeSymbol containingType, Document document, CompletionItem item, CancellationToken cancellationToken)
    {
        var syntaxFactory = document.GetRequiredLanguageService<SyntaxGenerator>();
        var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);
 
        var method = (IMethodSymbol)member;
        return CodeGenerationSymbolFactory.CreateMethodSymbol(
            attributes: [],
            accessibility: IncludeAccessibility(method, cancellationToken) ? method.DeclaredAccessibility : Accessibility.NotApplicable,
            modifiers: MemberInsertionCompletionItem.GetModifiers(item),
            returnType: method.ReturnType,
            refKind: method.RefKind,
            explicitInterfaceImplementations: default,
            name: member.Name,
            typeParameters: method.TypeParameters,
            parameters: method.Parameters.SelectAsArray(p => CodeGenerationSymbolFactory.CreateParameterSymbol(p.GetAttributes(), p.RefKind, p.IsParams, p.Type, p.Name)),
            statements: syntaxFactory.CreateThrowNotImplementedStatementBlock(semanticModel.Compilation));
    }
 
    protected async Task<IEnumerable<CompletionItem>?> CreatePartialItemsAsync(
        Document document, int position, TextSpan span, DeclarationModifiers modifiers, SyntaxToken token, CancellationToken cancellationToken)
    {
        var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);
 
        // Only inside classes and structs
        if (semanticModel.GetEnclosingSymbol(position, cancellationToken) is not INamedTypeSymbol enclosingSymbol)
            return null;
 
        if (enclosingSymbol.TypeKind is not (TypeKind.Struct or TypeKind.Class))
            return null;
 
        // https://github.com/dotnet/roslyn/issues/73772: should we also add an `AbstractPartialPropertiesProvider`?
        var symbols = semanticModel.LookupSymbols(position, container: enclosingSymbol)
                                    .OfType<IMethodSymbol>()
                                    .Where(m => IsPartial(m) && m.PartialImplementationPart == null);
 
        var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false);
        var line = text.Lines.IndexOf(position);
        var lineSpan = text.Lines.GetLineFromPosition(position).Span;
        return symbols.Select(s => CreateItem(s, line, span, semanticModel, modifiers, token));
    }
 
    private CompletionItem CreateItem(IMethodSymbol method, int line, TextSpan span, SemanticModel semanticModel, DeclarationModifiers modifiers, SyntaxToken token)
    {
        modifiers = new DeclarationModifiers(method.IsStatic, isUnsafe: method.RequiresUnsafeModifier(), isPartial: true, isAsync: modifiers.IsAsync);
        var displayText = GetDisplayText(method, semanticModel, span.Start);
 
        return MemberInsertionCompletionItem.Create(
            displayText,
            displayTextSuffix: "",
            modifiers,
            line,
            method,
            token,
            span.Start,
            rules: GetRules());
    }
}