AbstractRecommendationServiceBasedCompletionProvider.cs 6.3 KB
Newer Older
R
Ravi Chande 已提交
1 2 3
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

using Microsoft.CodeAnalysis.LanguageServices;
4 5 6 7 8
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Recommendations;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery;
using Microsoft.CodeAnalysis.Shared.Utilities;
R
Ravi Chande 已提交
9 10 11 12 13 14
using Roslyn.Utilities;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
15 16 17 18 19

namespace Microsoft.CodeAnalysis.Completion.Providers
{
    internal abstract class AbstractRecommendationServiceBasedCompletionProvider : AbstractSymbolCompletionProvider
    {
20
        protected override Task<ImmutableArray<ISymbol>> GetSymbolsWorker(SyntaxContext context, int position, OptionSet options, CancellationToken cancellationToken)
21 22 23 24 25
        {
            var recommender = context.GetLanguageService<IRecommendationService>();
            return recommender.GetRecommendedSymbolsAtPositionAsync(context.Workspace, context.SemanticModel, position, options, cancellationToken);
        }

26
        protected override async Task<ImmutableArray<ISymbol>> GetPreselectedSymbolsWorker(SyntaxContext context, int position, OptionSet options, CancellationToken cancellationToken)
27 28 29 30 31 32 33 34 35
        {
            var recommender = context.GetLanguageService<IRecommendationService>();
            var typeInferrer = context.GetLanguageService<ITypeInferenceService>();

            var inferredTypes = typeInferrer.InferTypes(context.SemanticModel, position, cancellationToken)
                .Where(t => t.SpecialType != SpecialType.System_Void)
                .ToSet();
            if (inferredTypes.Count == 0)
            {
36
                return ImmutableArray<ISymbol>.Empty;
37 38 39
            }

            var symbols = await recommender.GetRecommendedSymbolsAtPositionAsync(
40 41 42 43
                context.Workspace,
                context.SemanticModel,
                context.Position,
                options,
44
                cancellationToken).ConfigureAwait(false);
R
Ravi Chande 已提交
45

46 47 48
            // Don't preselect intrinsic type symbols so we can preselect their keywords instead. We will also ignore nullability for purposes of preselection
            // -- if a method is returning a string? but we've inferred we're assigning to a string or vice versa we'll still count those as the same.
            return symbols.WhereAsArray(s => inferredTypes.Contains(GetSymbolType(s), AllNullabilityIgnoringSymbolComparer.Instance) && !IsInstrinsic(s));
49 50 51 52
        }

        private ITypeSymbol GetSymbolType(ISymbol symbol)
        {
C
CyrusNajmabadi 已提交
53
            if (symbol is IMethodSymbol method)
54
            {
C
CyrusNajmabadi 已提交
55
                return method.ReturnType;
56 57 58 59 60
            }

            return symbol.GetSymbolType();
        }

61 62 63
        protected override CompletionItem CreateItem(
            string displayText, string displayTextSuffix, string insertionText,
            List<ISymbol> symbols, SyntaxContext context, bool preselect, SupportedPlatformData supportedPlatformData)
64
        {
65
            var rules = GetCompletionItemRules(symbols, context, preselect);
66 67 68
            var matchPriority = preselect ? ComputeSymbolMatchPriority(symbols[0]) : MatchPriority.Default;
            rules = rules.WithMatchPriority(matchPriority);

69 70 71 72 73
            if (context.IsRightSideOfNumericType)
            {
                rules = rules.WithSelectionBehavior(CompletionItemSelectionBehavior.SoftSelection);
            }
            else if (preselect)
74
            {
75
                rules = rules.WithSelectionBehavior(PreselectedItemSelectionBehavior);
76
            }
77

78
            return SymbolCompletionItem.CreateWithNameAndKind(
79
                displayText: displayText,
80
                displayTextSuffix: displayTextSuffix,
81 82 83
                symbols: symbols,
                rules: rules,
                contextPosition: context.Position,
84 85
                insertionText: insertionText,
                filterText: GetFilterText(symbols[0], displayText, context),
86
                supportedPlatforms: supportedPlatformData);
87 88
        }

C
CyrusNajmabadi 已提交
89
        protected abstract CompletionItemRules GetCompletionItemRules(List<ISymbol> symbols, SyntaxContext context, bool preselect);
90 91 92

        protected abstract CompletionItemSelectionBehavior PreselectedItemSelectionBehavior { get; }

R
Ravi Chande 已提交
93
        protected abstract bool IsInstrinsic(ISymbol symbol);
94 95 96 97 98

        private static int ComputeSymbolMatchPriority(ISymbol symbol)
        {
            if (symbol.MatchesKind(SymbolKind.Local, SymbolKind.Parameter, SymbolKind.RangeVariable))
            {
R
Ravi Chande 已提交
99
                return SymbolMatchPriority.PreferLocalOrParameterOrRangeVariable;
100 101 102 103 104 105 106 107 108 109 110 111 112 113
            }

            if (symbol.MatchesKind(SymbolKind.Field, SymbolKind.Property))
            {
                return SymbolMatchPriority.PreferFieldOrProperty;
            }

            if (symbol.MatchesKind(SymbolKind.Event, SymbolKind.Method))
            {
                return SymbolMatchPriority.PreferEventOrMethod;
            }

            return SymbolMatchPriority.PreferType;
        }
114

R
Ravi Chande 已提交
115 116
        protected override async Task<CompletionDescription> GetDescriptionWorkerAsync(
            Document document, CompletionItem item, CancellationToken cancellationToken)
117 118 119 120
        {
            var position = SymbolCompletionItem.GetContextPosition(item);
            var name = SymbolCompletionItem.GetSymbolName(item);
            var kind = SymbolCompletionItem.GetKind(item);
121 122 123 124 125
            var relatedDocumentIds = document.Project.Solution.GetRelatedDocumentIds(document.Id).Concat(document.Id);
            var options = document.Project.Solution.Workspace.Options;
            var totalSymbols = await base.GetPerContextSymbols(document, position, options, relatedDocumentIds, preselect: false, cancellationToken: cancellationToken).ConfigureAwait(false);
            foreach (var info in totalSymbols)
            {
C
CyrusNajmabadi 已提交
126
                var bestSymbols = info.symbols.Where(s => kind != null && s.Kind == kind && s.Name == name).ToImmutableArray();
127 128
                if (bestSymbols.Any())
                {
C
CyrusNajmabadi 已提交
129
                    return await SymbolCompletionItem.GetDescriptionAsync(item, bestSymbols, document, info.syntaxContext.SemanticModel, cancellationToken).ConfigureAwait(false);
130 131
                }
            }
132

133
            return CompletionDescription.Empty;
134
        }
135 136
    }
}