Skip to content

Commit

Permalink
Merge pull request #868 from DustinCampbell/completion-fixes
Browse files Browse the repository at this point in the history
Don't set completion text to full signature unless its override or partial method completion
  • Loading branch information
DustinCampbell authored May 19, 2017
2 parents 2ac2824 + 62b361e commit 30a3cf2
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
Expand All @@ -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<IEnumerable<ISymbol>> GetCompletionSymbolsAsync(this CompletionItem completionItem, IEnumerable<ISymbol> 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
Expand All @@ -43,5 +56,16 @@ public static async Task<IEnumerable<ISymbol>> GetCompletionSymbolsAsync(this Co

return Enumerable.Empty<ISymbol>();
}

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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,31 @@ public async Task<IEnumerable<AutoCompleteResponse>> 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));
}
}
}
Expand Down Expand Up @@ -104,50 +117,60 @@ public async Task<IEnumerable<AutoCompleteResponse>> Handle(AutoCompleteRequest
.ThenBy(c => c.CompletionText, StringComparer.OrdinalIgnoreCase);
}

private IEnumerable<AutoCompleteResponse> MakeSnippetedResponses(AutoCompleteRequest request, ISymbol symbol, string displayName)
private IEnumerable<AutoCompleteResponse> 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<AutoCompleteResponse> MakeSnippetedResponses(AutoCompleteRequest request, IMethodSymbol methodSymbol, string completionText)
{
var completions = new List<AutoCompleteResponse>();

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<AutoCompleteResponse> MakeSnippetedResponses(AutoCompleteRequest request, INamedTypeSymbol typeSymbol, string completionText)
{
var completions = new List<AutoCompleteResponse>
{
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);
Expand Down
16 changes: 14 additions & 2 deletions src/OmniSharp.Roslyn/Extensions/SymbolExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down

0 comments on commit 30a3cf2

Please sign in to comment.