|
// 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());
}
}
|