| File: Completion\Providers\AbstractPartialTypeCompletionProvider.cs | Web Access |
| Project: src\roslyn\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; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Completion.Providers; internal abstract partial class AbstractPartialTypeCompletionProvider<TSyntaxContext> : LSPCompletionProvider where TSyntaxContext : SyntaxContext { protected AbstractPartialTypeCompletionProvider() { } public sealed override async Task ProvideCompletionsAsync(CompletionContext completionContext) { try { var document = completionContext.Document; var position = completionContext.Position; var cancellationToken = completionContext.CancellationToken; var tree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var node = GetPartialTypeSyntaxNode(tree, position, cancellationToken); if (node != null) { var semanticModel = await document.ReuseExistingSpeculativeModelAsync(node, cancellationToken).ConfigureAwait(false); if (semanticModel.GetDeclaredSymbol(node, cancellationToken) is INamedTypeSymbol declaredSymbol) { var syntaxContextService = document.GetRequiredLanguageService<ISyntaxContextService>(); var syntaxContext = (TSyntaxContext)syntaxContextService.CreateContext(document, semanticModel, position, cancellationToken); var symbols = LookupCandidateSymbols(syntaxContext, declaredSymbol, cancellationToken); var items = symbols?.Select(s => CreateCompletionItem(s, syntaxContext)); if (items != null) { completionContext.AddItems(items); } } } } catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, ErrorSeverity.General)) { // nop } } private CompletionItem CreateCompletionItem(INamedTypeSymbol symbol, TSyntaxContext context) { var (displayText, suffix, insertionText) = GetDisplayAndSuffixAndInsertionText(symbol, context); return SymbolCompletionItem.CreateWithSymbolId( displayText: displayText, displayTextSuffix: suffix, insertionText: insertionText, symbols: [symbol], contextPosition: context.Position, properties: GetProperties(symbol, context), rules: CompletionItemRules.Default); } protected abstract ImmutableArray<KeyValuePair<string, string>> GetProperties(INamedTypeSymbol symbol, TSyntaxContext context); protected abstract SyntaxNode? GetPartialTypeSyntaxNode(SyntaxTree tree, int position, CancellationToken cancellationToken); protected abstract (string displayText, string suffix, string insertionText) GetDisplayAndSuffixAndInsertionText(INamedTypeSymbol symbol, TSyntaxContext context); protected virtual IEnumerable<INamedTypeSymbol>? LookupCandidateSymbols(TSyntaxContext context, INamedTypeSymbol declaredSymbol, CancellationToken cancellationToken) { if (declaredSymbol == null) { throw new ArgumentNullException(nameof(declaredSymbol)); } var semanticModel = context.SemanticModel; if (declaredSymbol.ContainingSymbol is not INamespaceOrTypeSymbol containingSymbol) return []; return semanticModel.LookupNamespacesAndTypes(context.Position, containingSymbol) .OfType<INamedTypeSymbol>() .Where(symbol => declaredSymbol.TypeKind == symbol.TypeKind && NotNewDeclaredMember(symbol, context) && InSameProject(symbol, semanticModel.Compilation)); } private static bool InSameProject(INamedTypeSymbol symbol, Compilation compilation) => symbol.DeclaringSyntaxReferences.Any(static (r, compilation) => compilation.SyntaxTrees.Contains(r.SyntaxTree), compilation); private static bool NotNewDeclaredMember(INamedTypeSymbol symbol, TSyntaxContext context) { return symbol.DeclaringSyntaxReferences .Select(reference => reference.GetSyntax()) .Any(node => !(node.SyntaxTree == context.SyntaxTree && node.Span.IntersectsWith(context.Position))); } internal override Task<CompletionDescription> GetDescriptionWorkerAsync(Document document, CompletionItem item, CompletionOptions options, SymbolDescriptionOptions displayOptions, CancellationToken cancellationToken) => SymbolCompletionItem.GetDescriptionAsync(item, document, displayOptions, cancellationToken); public override Task<TextChange?> GetTextChangeAsync(Document document, CompletionItem selectedItem, char? ch, CancellationToken cancellationToken) { var insertionText = SymbolCompletionItem.GetInsertionText(selectedItem); return Task.FromResult<TextChange?>(new TextChange(selectedItem.Span, insertionText)); } }