diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Intellisense/CompletionItemExtensions.cs b/src/OmniSharp.Roslyn.CSharp/Services/Intellisense/CompletionItemExtensions.cs index 703c3ff72a..bddfe62cf3 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Intellisense/CompletionItemExtensions.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Intellisense/CompletionItemExtensions.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Reflection; @@ -13,24 +12,38 @@ namespace OmniSharp.Roslyn.CSharp.Services.Intellisense { internal static class CompletionItemExtensions { + private const string GetSymbolsAsync = nameof(GetSymbolsAsync); + private const string InsertionText = nameof(InsertionText); + private const string NamedParameterCompletionProvider = "Microsoft.CodeAnalysis.CSharp.Completion.Providers.NamedParameterCompletionProvider"; + private const string OverrideCompletionProvider = "Microsoft.CodeAnalysis.CSharp.Completion.Providers.OverrideCompletionProvider"; + private const string ParitalMethodCompletionProvider = "Microsoft.CodeAnalysis.CSharp.Completion.Providers.PartialMethodCompletionProvider"; + private const string Provider = nameof(Provider); + private const string SymbolCompletionItem = "Microsoft.CodeAnalysis.Completion.Providers.SymbolCompletionItem"; + private const string SymbolCompletionProvider = "Microsoft.CodeAnalysis.CSharp.Completion.Providers.SymbolCompletionProvider"; + private const string SymbolKind = nameof(SymbolKind); + private const string SymbolName = nameof(SymbolName); + private const string Symbols = nameof(Symbols); + private static MethodInfo _getSymbolsAsync; static CompletionItemExtensions() { - var symbolCompletionItemType = typeof(CompletionItem).GetTypeInfo().Assembly.GetType("Microsoft.CodeAnalysis.Completion.Providers.SymbolCompletionItem"); - _getSymbolsAsync = symbolCompletionItemType.GetMethod("GetSymbolsAsync", BindingFlags.Public | BindingFlags.Static); + var symbolCompletionItemType = typeof(CompletionItem).GetTypeInfo().Assembly.GetType(SymbolCompletionItem); + _getSymbolsAsync = symbolCompletionItemType.GetMethod(GetSymbolsAsync, BindingFlags.Public | BindingFlags.Static); } public static async Task> GetCompletionSymbolsAsync(this CompletionItem completionItem, IEnumerable recommendedSymbols, Document document) { + var properties = completionItem.Properties; + // for SymbolCompletionProvider, use the logic of extracting information from recommended symbols - if (completionItem.Properties.ContainsKey("Provider") && completionItem.Properties["Provider"] == "Microsoft.CodeAnalysis.CSharp.Completion.Providers.SymbolCompletionProvider") + if (properties.TryGetValue(Provider, out var provider) && provider == SymbolCompletionProvider) { - return recommendedSymbols.Where(x => x.Name == completionItem.Properties["SymbolName"] && (int)x.Kind == int.Parse(completionItem.Properties["SymbolKind"])).Distinct(); + return recommendedSymbols.Where(x => x.Name == properties[SymbolName] && (int)x.Kind == int.Parse(properties[SymbolKind])).Distinct(); } // if the completion provider encoded symbols into Properties, we can return them - if (completionItem.Properties.ContainsKey("Symbols")) + if (properties.ContainsKey(Symbols)) { // the API to decode symbols is not public at the moment // http://source.roslyn.io/#Microsoft.CodeAnalysis.Features/Completion/Providers/SymbolCompletionItem.cs,93 @@ -43,5 +56,16 @@ public static async Task> GetCompletionSymbolsAsync(this Co return Enumerable.Empty(); } + + public static bool UseDisplayTextAsCompletionText(this CompletionItem completionItem) + { + return completionItem.Properties.TryGetValue(Provider, out var provider) + && (provider == NamedParameterCompletionProvider || provider == OverrideCompletionProvider || provider == ParitalMethodCompletionProvider); + } + + public static bool TryGetInsertionText(this CompletionItem completionItem, out string insertionText) + { + return completionItem.Properties.TryGetValue(InsertionText, out insertionText); + } } } diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Intellisense/IntellisenseService.cs b/src/OmniSharp.Roslyn.CSharp/Services/Intellisense/IntellisenseService.cs index 6383d200c3..71d534e5d8 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Intellisense/IntellisenseService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Intellisense/IntellisenseService.cs @@ -57,18 +57,31 @@ public async Task> Handle(AutoCompleteRequest { foreach (var symbol in symbols) { + if (item.UseDisplayTextAsCompletionText()) + { + completionText = item.DisplayText; + } + else if (item.TryGetInsertionText(out var insertionText)) + { + completionText = insertionText; + } + else + { + completionText = symbol.Name; + } + if (symbol != null) { if (request.WantSnippet) { - foreach (var completion in MakeSnippetedResponses(request, symbol, item.DisplayText)) + foreach (var completion in MakeSnippetedResponses(request, symbol, completionText)) { completions.Add(completion); } } else { - completions.Add(MakeAutoCompleteResponse(request, symbol, item.DisplayText)); + completions.Add(MakeAutoCompleteResponse(request, symbol, completionText)); } } } @@ -104,50 +117,60 @@ public async Task> Handle(AutoCompleteRequest .ThenBy(c => c.CompletionText, StringComparer.OrdinalIgnoreCase); } - private IEnumerable MakeSnippetedResponses(AutoCompleteRequest request, ISymbol symbol, string displayName) + private IEnumerable MakeSnippetedResponses(AutoCompleteRequest request, ISymbol symbol, string completionText) + { + switch (symbol) + { + case IMethodSymbol methodSymbol: + return MakeSnippetedResponses(request, methodSymbol, completionText); + case INamedTypeSymbol typeSymbol: + return MakeSnippetedResponses(request, typeSymbol, completionText); + + default: + return new[] { MakeAutoCompleteResponse(request, symbol, completionText) }; + } + } + + private IEnumerable MakeSnippetedResponses(AutoCompleteRequest request, IMethodSymbol methodSymbol, string completionText) { var completions = new List(); - var methodSymbol = symbol as IMethodSymbol; - if (methodSymbol != null) + if (methodSymbol.Parameters.Any(p => p.IsOptional)) { - if (methodSymbol.Parameters.Any(p => p.IsOptional)) - { - completions.Add(MakeAutoCompleteResponse(request, symbol, displayName, false)); - } + completions.Add(MakeAutoCompleteResponse(request, methodSymbol, completionText, includeOptionalParams: false)); + } - completions.Add(MakeAutoCompleteResponse(request, symbol, displayName)); + completions.Add(MakeAutoCompleteResponse(request, methodSymbol, completionText)); - return completions; - } + return completions; + } - var typeSymbol = symbol as INamedTypeSymbol; - if (typeSymbol != null) + private IEnumerable MakeSnippetedResponses(AutoCompleteRequest request, INamedTypeSymbol typeSymbol, string completionText) + { + var completions = new List { - completions.Add(MakeAutoCompleteResponse(request, symbol, displayName)); + MakeAutoCompleteResponse(request, typeSymbol, completionText) + }; - if (typeSymbol.TypeKind != TypeKind.Enum) + if (typeSymbol.TypeKind != TypeKind.Enum) + { + foreach (var ctor in typeSymbol.InstanceConstructors) { - foreach (var ctor in typeSymbol.InstanceConstructors) - { - completions.Add(MakeAutoCompleteResponse(request, ctor, displayName)); - } + completions.Add(MakeAutoCompleteResponse(request, ctor, completionText)); } - - return completions; } - return new[] { MakeAutoCompleteResponse(request, symbol, displayName) }; + return completions; } - private AutoCompleteResponse MakeAutoCompleteResponse(AutoCompleteRequest request, ISymbol symbol, string displayName, bool includeOptionalParams = true) + private AutoCompleteResponse MakeAutoCompleteResponse(AutoCompleteRequest request, ISymbol symbol, string completionText, bool includeOptionalParams = true) { var displayNameGenerator = new SnippetGenerator(); displayNameGenerator.IncludeMarkers = false; displayNameGenerator.IncludeOptionalParameters = includeOptionalParams; var response = new AutoCompleteResponse(); - response.CompletionText = displayName; + response.CompletionText = completionText; // TODO: Do something more intelligent here response.DisplayText = displayNameGenerator.Generate(symbol); diff --git a/src/OmniSharp.Roslyn/Extensions/SymbolExtensions.cs b/src/OmniSharp.Roslyn/Extensions/SymbolExtensions.cs index f1f4f9401d..9f4281c681 100644 --- a/src/OmniSharp.Roslyn/Extensions/SymbolExtensions.cs +++ b/src/OmniSharp.Roslyn/Extensions/SymbolExtensions.cs @@ -7,11 +7,23 @@ public static class SymbolExtensions { public static string GetKind(this ISymbol symbol) { - var namedType = symbol as INamedTypeSymbol; - if (namedType != null) + if (symbol is INamedTypeSymbol namedType) { return Enum.GetName(namedType.TypeKind.GetType(), namedType.TypeKind); } + + if (symbol.Kind == SymbolKind.Field && + symbol.ContainingType?.TypeKind == TypeKind.Enum && + symbol.Name != WellKnownMemberNames.EnumBackingFieldName) + { + return "EnumMember"; + } + + if ((symbol as IFieldSymbol)?.IsConst == true) + { + return "Const"; + } + return Enum.GetName(symbol.Kind.GetType(), symbol.Kind); } }