diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/AttributeNamedParameterCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/AttributeNamedParameterCompletionProvider.cs index 77cda7299defb..77ea3a9a0b88a 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/AttributeNamedParameterCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/AttributeNamedParameterCompletionProvider.cs @@ -145,7 +145,7 @@ private async Task> GetNameEqualsItemsAsync( var text = await semanticModel.SyntaxTree.GetTextAsync(context.CancellationToken).ConfigureAwait(false); var q = from p in attributeNamedParameters where !existingNamedParameters.Contains(p.Name) - select SymbolCompletionItem.Create( + select SymbolCompletionItem.CreateWithSymbolId( displayText: p.Name.ToIdentifierToken().ToString() + SpaceEqualsString, insertionText: null, symbol: p, @@ -165,7 +165,7 @@ private async Task> GetNameColonItemsAsync( return from pl in parameterLists from p in pl where !existingNamedParameters.Contains(p.Name) - select SymbolCompletionItem.Create( + select SymbolCompletionItem.CreateWithSymbolId( displayText: p.Name.ToIdentifierToken().ToString() + ColonString, insertionText: null, symbol: p, diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/CrefCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/CrefCompletionProvider.cs index 547fe28274c00..259bb80804a8a 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/CrefCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/CrefCompletionProvider.cs @@ -19,8 +19,9 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.Providers { - internal sealed class CrefCompletionProvider : CommonCompletionProvider + internal sealed class CrefCompletionProvider : AbstractCrefCompletionProvider { + public static readonly SymbolDisplayFormat QualifiedCrefFormat = new SymbolDisplayFormat( globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted, @@ -60,10 +61,33 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) var options = context.Options; var cancellationToken = context.CancellationToken; + var (token, semanticModel, symbols) = await GetSymbolsAsync(document, position, options, cancellationToken).ConfigureAwait(false); + + if (symbols.Length == 0) + { + return; + } + + context.IsExclusive = true; + + var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + var span = GetCompletionItemSpan(text, position); + var hideAdvancedMembers = options.GetOption(CompletionOptions.HideAdvancedMembers, semanticModel.Language); + var serializedOptions = ImmutableDictionary.Empty.Add(HideAdvancedMembers, hideAdvancedMembers.ToString()); + + var items = CreateCompletionItems(document.Project.Solution.Workspace, + semanticModel, symbols, token, span, position, serializedOptions); + + context.AddItems(items); + } + + protected override async Task<(SyntaxToken, SemanticModel, ImmutableArray)> GetSymbolsAsync( + Document document, int position, OptionSet options, CancellationToken cancellationToken) + { var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); if (!tree.IsEntirelyWithinCrefSyntax(position, cancellationToken)) { - return; + return (default(SyntaxToken), null, ImmutableArray.Empty); } var token = tree.FindTokenOnLeftOfPosition(position, cancellationToken, includeDocumentationComments: true) @@ -75,28 +99,18 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) _testSpeculativeNodeCallbackOpt?.Invoke(parentNode); if (parentNode == null) { - return; + return (default(SyntaxToken), null, ImmutableArray.Empty); } var semanticModel = await document.GetSemanticModelForNodeAsync( parentNode, cancellationToken).ConfigureAwait(false); - var symbols = GetSymbols(token, semanticModel, cancellationToken); - - symbols = symbols.FilterToVisibleAndBrowsableSymbols(options.GetOption(CompletionOptions.HideAdvancedMembers, semanticModel.Language), semanticModel.Compilation); - - if (!symbols.Any()) - { - return; - } - - context.IsExclusive = true; - - var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - var span = GetCompletionItemSpan(text, position); + var symbols = GetSymbols(token, semanticModel, cancellationToken) + .FilterToVisibleAndBrowsableSymbols( + options.GetOption(CompletionOptions.HideAdvancedMembers, semanticModel.Language), + semanticModel.Compilation); - var items = CreateCompletionItems(document.Project.Solution.Workspace, semanticModel, symbols, token, span); - context.AddItems(items); + return (token, semanticModel, symbols); } private static bool IsCrefStartContext(SyntaxToken token) @@ -230,7 +244,7 @@ private static TextSpan GetCompletionItemSpan(SourceText text, int position) } private IEnumerable CreateCompletionItems( - Workspace workspace, SemanticModel semanticModel, IEnumerable symbols, SyntaxToken token, TextSpan itemSpan) + Workspace workspace, SemanticModel semanticModel, IEnumerable symbols, SyntaxToken token, TextSpan itemSpan, int position, ImmutableDictionary options) { var builder = SharedPools.Default().Allocate(); try @@ -238,7 +252,7 @@ private IEnumerable CreateCompletionItems( foreach (var symbol in symbols) { builder.Clear(); - yield return CreateItem(workspace, semanticModel, symbol, token, builder); + yield return CreateItem(workspace, semanticModel, symbol, token, position, builder, options); } } finally @@ -248,10 +262,8 @@ private IEnumerable CreateCompletionItems( } private CompletionItem CreateItem( - Workspace workspace, SemanticModel semanticModel, ISymbol symbol, SyntaxToken token, StringBuilder builder) + Workspace workspace, SemanticModel semanticModel, ISymbol symbol, SyntaxToken token, int position, StringBuilder builder, ImmutableDictionary options) { - int position = token.SpanStart; - if (symbol is INamespaceOrTypeSymbol && token.IsKind(SyntaxKind.DotToken)) { // Handle qualified namespace and type names. @@ -262,7 +274,7 @@ private CompletionItem CreateItem( { // Handle unqualified namespace and type names, or member names. - builder.Append(symbol.ToMinimalDisplayString(semanticModel, position, CrefFormat)); + builder.Append(symbol.ToMinimalDisplayString(semanticModel, token.SpanStart, CrefFormat)); var parameters = symbol.GetParameters(); if (!parameters.IsDefaultOrEmpty) @@ -303,18 +315,16 @@ private CompletionItem CreateItem( .Replace('>', '}') .ToString(); - return SymbolCompletionItem.Create( + return SymbolCompletionItem.CreateWithNameAndKind( displayText: insertionText, insertionText: insertionText, - symbol: symbol, + symbols: ImmutableArray.Create(symbol), contextPosition: position, sortText: symbolText, + properties: options, rules: GetRules(insertionText)); } - protected override Task GetDescriptionWorkerAsync(Document document, CompletionItem item, CancellationToken cancellationToken) - => SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken); - private static readonly CharacterSetModificationRule s_WithoutOpenBrace = CharacterSetModificationRule.Create(CharacterSetModificationKind.Remove, '{'); private static readonly CharacterSetModificationRule s_WithoutOpenParen = CharacterSetModificationRule.Create(CharacterSetModificationKind.Remove, '('); @@ -342,7 +352,6 @@ private CompletionItemRules GetRules(string displayText) } } - private static readonly string InsertionTextProperty = "insertionText"; protected override Task GetTextChangeAsync(CompletionItem selectedItem, char? ch, CancellationToken cancellationToken) diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/EnumAndCompletionListTagCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/EnumAndCompletionListTagCompletionProvider.cs index 6df94a6f6e53d..f358c28ba9bcd 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/EnumAndCompletionListTagCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/EnumAndCompletionListTagCompletionProvider.cs @@ -111,7 +111,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) var workspace = document.Project.Solution.Workspace; var text = await semanticModel.SyntaxTree.GetTextAsync(cancellationToken).ConfigureAwait(false); - var item = SymbolCompletionItem.Create( + var item = SymbolCompletionItem.CreateWithSymbolId( displayText: displayText, insertionText: null, symbol: alias ?? type, diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceCompletionProvider.cs index 38d95f3f66d61..9cb8e79ba3d65 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceCompletionProvider.cs @@ -96,7 +96,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) semanticModel, namePosition, s_signatureDisplayFormat); var insertionText = displayText; - var item = SymbolCompletionItem.Create( + var item = SymbolCompletionItem.CreateWithSymbolId( displayText, insertionText: insertionText, symbol: member, diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/NamedParameterCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/NamedParameterCompletionProvider.cs index 7518ab8e01fa8..9dc67c4462bf8 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/NamedParameterCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/NamedParameterCompletionProvider.cs @@ -95,7 +95,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) // exact match. var escapedName = parameter.Name.ToIdentifierToken().ToString(); - context.AddItem(SymbolCompletionItem.Create( + context.AddItem(SymbolCompletionItem.CreateWithSymbolId( displayText: escapedName + ColonString, insertionText: null, symbol: parameter, diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/SymbolCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/SymbolCompletionProvider.cs index 1e2a182b9adc8..0191ac0b8ba37 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/SymbolCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/SymbolCompletionProvider.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion; diff --git a/src/Features/CSharp/Portable/project.json b/src/Features/CSharp/Portable/project.json index a5bfa3b918f93..e75a1bda99d17 100644 --- a/src/Features/CSharp/Portable/project.json +++ b/src/Features/CSharp/Portable/project.json @@ -1,5 +1,7 @@ { - "dependencies": { }, + "dependencies": { + "System.ValueTuple": "4.3.0" + }, "frameworks": { "netstandard1.3": { "imports": [ "portable-net45+win8", "dotnet" ] diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractCrefCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractCrefCompletionProvider.cs new file mode 100644 index 0000000000000..48019b726c55e --- /dev/null +++ b/src/Features/Core/Portable/Completion/Providers/AbstractCrefCompletionProvider.cs @@ -0,0 +1,38 @@ +// 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.Options; +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.Completion.Providers +{ + abstract class AbstractCrefCompletionProvider : CommonCompletionProvider + { + protected const string HideAdvancedMembers = nameof(HideAdvancedMembers); + + protected override async Task GetDescriptionWorkerAsync(Document document, CompletionItem item, CancellationToken cancellationToken) + { + var position = SymbolCompletionItem.GetContextPosition(item); + + // What EditorBrowsable settings were we previously passed in (if it mattered)? + bool hideAdvancedMembers = false; + if (item.Properties.TryGetValue(HideAdvancedMembers, out var hideAdvancedMembersString)) + { + bool.TryParse(hideAdvancedMembersString, out hideAdvancedMembers); + } + + var options = document.Project.Solution.Workspace.Options + .WithChangedOption(new OptionKey(CompletionOptions.HideAdvancedMembers, document.Project.Language), hideAdvancedMembers); + + var (token, semanticModel, symbols) = await GetSymbolsAsync(document, position, options, cancellationToken).ConfigureAwait(false); + var name = SymbolCompletionItem.GetSymbolName(item); + var kind = SymbolCompletionItem.GetKind(item); + var bestSymbols = symbols.WhereAsArray(s => s.Kind == kind && s.Name == name); + return await SymbolCompletionItem.GetDescriptionAsync(item, bestSymbols, document, semanticModel, cancellationToken).ConfigureAwait(false); + } + + protected abstract Task<(SyntaxToken, SemanticModel, ImmutableArray)> GetSymbolsAsync( + Document document, int position, OptionSet options, CancellationToken cancellationToken); + } +} diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractObjectCreationCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractObjectCreationCompletionProvider.cs index e48c16881c833..6c39490641f09 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractObjectCreationCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractObjectCreationCompletionProvider.cs @@ -28,7 +28,7 @@ protected override CompletionItem CreateItem( SyntaxContext context, bool preselect, SupportedPlatformData supportedPlatformData) { - return SymbolCompletionItem.Create( + return SymbolCompletionItem.CreateWithSymbolId( displayText: displayText, insertionText: insertionText, filterText: GetFilterText(symbols[0], displayText, context), diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractObjectInitializerCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractObjectInitializerCompletionProvider.cs index 2a84fecbca0d3..c52bf2f4fef6c 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractObjectInitializerCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractObjectInitializerCompletionProvider.cs @@ -61,7 +61,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) foreach (var uninitializedMember in uninitializedMembers) { - context.AddItem(SymbolCompletionItem.Create( + context.AddItem(SymbolCompletionItem.CreateWithSymbolId( displayText: uninitializedMember.Name, insertionText: null, symbol: uninitializedMember, diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractPartialTypeCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractPartialTypeCompletionProvider.cs index c1bc79786db8a..ca1b02c03239d 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractPartialTypeCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractPartialTypeCompletionProvider.cs @@ -53,7 +53,7 @@ private CompletionItem CreateCompletionItem( { var displayAndInsertionText = GetDisplayAndInsertionText(symbol, context); - return SymbolCompletionItem.Create( + return SymbolCompletionItem.CreateWithSymbolId( displayText: displayAndInsertionText.Item1, insertionText: displayAndInsertionText.Item2, symbol: symbol, diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractRecommendationServiceBasedCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractRecommendationServiceBasedCompletionProvider.cs index 7acf36d135827..bdeff78e9bb2f 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractRecommendationServiceBasedCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractRecommendationServiceBasedCompletionProvider.cs @@ -1,15 +1,18 @@ -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; +// 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; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Recommendations; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery; using Microsoft.CodeAnalysis.Shared.Utilities; +using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; namespace Microsoft.CodeAnalysis.Completion.Providers { @@ -35,10 +38,10 @@ protected override async Task> GetPreselectedSymbolsWork } var symbols = await recommender.GetRecommendedSymbolsAtPositionAsync( - context.Workspace, - context.SemanticModel, - position, - options, + context.Workspace, + context.SemanticModel, + context.Position, + options, cancellationToken).ConfigureAwait(false); // Don't preselect intrinsic type symbols so we can preselect their keywords instead. @@ -64,7 +67,7 @@ protected override CompletionItem CreateItem(string displayText, string insertio rules = rules.WithSelectionBehavior(PreselectedItemSelectionBehavior); } - return SymbolCompletionItem.Create( + return SymbolCompletionItem.CreateWithNameAndKind( displayText: displayText, insertionText: insertionText, filterText: GetFilterText(symbols[0], displayText, context), @@ -100,5 +103,26 @@ private static int ComputeSymbolMatchPriority(ISymbol symbol) return SymbolMatchPriority.PreferType; } + + protected override async Task GetDescriptionWorkerAsync( + Document document, CompletionItem item, CancellationToken cancellationToken) + { + var position = SymbolCompletionItem.GetContextPosition(item); + var name = SymbolCompletionItem.GetSymbolName(item); + var kind = SymbolCompletionItem.GetKind(item); + 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) + { + var bestSymbols = info.Item3.Where(s => kind != null && s.Kind == kind && s.Name == name).ToImmutableArray(); + if (bestSymbols.Any()) + { + return await SymbolCompletionItem.GetDescriptionAsync(item, bestSymbols, document, info.Item2.SemanticModel, cancellationToken).ConfigureAwait(false); + } + } + + return CompletionDescription.Empty; + } } } diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractSymbolCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractSymbolCompletionProvider.cs index 946e3f70c6e7f..71ce0850fb208 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractSymbolCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractSymbolCompletionProvider.cs @@ -118,7 +118,7 @@ protected virtual CompletionItem CreateItem( SyntaxContext context, bool preselect, SupportedPlatformData supportedPlatformData) { - return SymbolCompletionItem.Create( + return SymbolCompletionItem.CreateWithSymbolId( displayText: displayText, insertionText: insertionText, filterText: GetFilterText(symbols[0], displayText, context), @@ -129,9 +129,6 @@ protected virtual CompletionItem CreateItem( rules: GetCompletionItemRules(symbols, context)); } - protected override Task GetDescriptionWorkerAsync(Document document, CompletionItem item, CancellationToken cancellationToken) - => SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken); - protected virtual string GetFilterText(ISymbol symbol, string displayText, SyntaxContext context) { return (displayText == symbol.Name) || @@ -148,6 +145,9 @@ protected virtual Task> GetPreselectedSymbolsWorker(Synt return SpecializedTasks.EmptyImmutableArray(); } + protected override Task GetDescriptionWorkerAsync(Document document, CompletionItem item, CancellationToken cancellationToken) + => SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken); + public override async Task ProvideCompletionsAsync(CompletionContext context) { var document = context.Document; diff --git a/src/Features/Core/Portable/Completion/Providers/MemberInsertingCompletionItem.cs b/src/Features/Core/Portable/Completion/Providers/MemberInsertingCompletionItem.cs index 89460b53e79d0..9e40b54bbb508 100644 --- a/src/Features/Core/Portable/Completion/Providers/MemberInsertingCompletionItem.cs +++ b/src/Features/Core/Portable/Completion/Providers/MemberInsertingCompletionItem.cs @@ -24,7 +24,7 @@ public static CompletionItem Create( .Add("Modifiers", modifiers.ToString()) .Add("TokenSpanEnd", token.Span.End.ToString()); - return SymbolCompletionItem.Create( + return SymbolCompletionItem.CreateWithSymbolId( displayText: displayText, symbol: symbol, glyph: glyph, diff --git a/src/Features/Core/Portable/Completion/Providers/SymbolCompletionItem.cs b/src/Features/Core/Portable/Completion/Providers/SymbolCompletionItem.cs index 5d30fbf147c7f..6ba7a2050ab99 100644 --- a/src/Features/Core/Portable/Completion/Providers/SymbolCompletionItem.cs +++ b/src/Features/Core/Portable/Completion/Providers/SymbolCompletionItem.cs @@ -11,11 +11,12 @@ namespace Microsoft.CodeAnalysis.Completion.Providers { - internal static class SymbolCompletionItem + internal static partial class SymbolCompletionItem { - public static CompletionItem Create( + private static CompletionItem CreateWorker( string displayText, IReadOnlyList symbols, + Func, CompletionItem, CompletionItem> symbolEncoder, int contextPosition = -1, string sortText = null, string insertionText = null, @@ -29,8 +30,6 @@ public static CompletionItem Create( { var props = properties ?? ImmutableDictionary.Empty; - props = props.Add("Symbols", EncodeSymbols(symbols)); - if (insertionText != null) { props = props.Add("InsertionText", insertionText); @@ -52,12 +51,31 @@ public static CompletionItem Create( tags: tags, rules: rules); - return WithSupportedPlatforms(item, supportedPlatforms); + item = WithSupportedPlatforms(item, supportedPlatforms); + return symbolEncoder(symbols, item); + } + + public static CompletionItem AddSymbolEncoding(IReadOnlyList symbols, CompletionItem item) + { + return item.AddProperty("Symbols", EncodeSymbols(symbols)); + } + + public static CompletionItem AddSymbolEncoding(ISymbol symbol, CompletionItem item) + { + return item.AddProperty("Symbols", EncodeSymbol(symbol)); } - public static CompletionItem Create( + public static CompletionItem AddSymbolNameAndKind(IReadOnlyList symbols, CompletionItem item) + { + var symbol = symbols[0]; + return item.AddProperty("SymbolKind", ((int)symbol.Kind).ToString()) + .AddProperty("SymbolName", symbol.Name); + } + + private static CompletionItem CreateWorker( string displayText, ISymbol symbol, + Func, CompletionItem, CompletionItem> symbolEncoder, int contextPosition = -1, string sortText = null, string insertionText = null, @@ -68,9 +86,10 @@ public static CompletionItem Create( ImmutableDictionary properties = null, CompletionItemRules rules = null) { - return Create( + return CreateWorker( displayText: displayText, symbols: ImmutableArray.Create(symbol), + symbolEncoder: symbolEncoder, contextPosition: contextPosition, sortText: sortText, insertionText: insertionText, @@ -176,16 +195,7 @@ public static async Task GetDescriptionAsync(CompletionIt var supportedPlatforms = GetSupportedPlatforms(item, workspace); - // find appropriate document for descripton context - var contextDocument = document; - if (supportedPlatforms != null && supportedPlatforms.InvalidProjects.Contains(document.Id.ProjectId)) - { - var contextId = document.GetLinkedDocumentIds().FirstOrDefault(id => !supportedPlatforms.InvalidProjects.Contains(id.ProjectId)); - if (contextId != null) - { - contextDocument = document.Project.Solution.GetDocument(contextId); - } - } + var contextDocument = FindAppropriateDocumentForDescriptionContext(document, supportedPlatforms); var semanticModel = await contextDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var symbols = await GetSymbolsAsync(item, document, cancellationToken).ConfigureAwait(false); @@ -199,6 +209,21 @@ public static async Task GetDescriptionAsync(CompletionIt } } + private static Document FindAppropriateDocumentForDescriptionContext(Document document, SupportedPlatformData supportedPlatforms) + { + var contextDocument = document; + if (supportedPlatforms != null && supportedPlatforms.InvalidProjects.Contains(document.Id.ProjectId)) + { + var contextId = document.GetLinkedDocumentIds().FirstOrDefault(id => !supportedPlatforms.InvalidProjects.Contains(id.ProjectId)); + if (contextId != null) + { + contextDocument = document.Project.Solution.GetDocument(contextId); + } + } + + return contextDocument; + } + private static CompletionItem WithSupportedPlatforms(CompletionItem completionItem, SupportedPlatformData supportedPlatforms) { if (supportedPlatforms != null) @@ -216,7 +241,7 @@ private static CompletionItem WithSupportedPlatforms(CompletionItem completionIt private static readonly char[] projectSeperators = new[] { ';' }; public static SupportedPlatformData GetSupportedPlatforms(CompletionItem item, Workspace workspace) { - if (item.Properties.TryGetValue("InvalidProjects", out var invalidProjects) + if (item.Properties.TryGetValue("InvalidProjects", out var invalidProjects) && item.Properties.TryGetValue("CandidateProjects", out var candidateProjects)) { return new SupportedPlatformData( @@ -250,5 +275,97 @@ public static string GetInsertionText(CompletionItem item) item.Properties.TryGetValue("InsertionText", out var text); return text; } + + public static CompletionItem CreateWithSymbolId( + string displayText, + IReadOnlyList symbols, + int contextPosition, + string sortText = null, + string insertionText = null, + Glyph? glyph = null, + string filterText = null, + int? matchPriority = null, + SupportedPlatformData supportedPlatforms = null, + ImmutableDictionary properties = null, + ImmutableArray tags = default(ImmutableArray), + CompletionItemRules rules = null) + { + return CreateWorker(displayText, symbols, AddSymbolEncoding, contextPosition, sortText, insertionText, glyph, + filterText, matchPriority, supportedPlatforms, properties, tags, rules); + } + + public static CompletionItem CreateWithSymbolId( + string displayText, + ISymbol symbol, + int contextPosition, + string sortText = null, + string insertionText = null, + Glyph? glyph = null, + string filterText = null, + int? matchPriority = null, + SupportedPlatformData supportedPlatforms = null, + ImmutableDictionary properties = null, + CompletionItemRules rules = null) + { + return CreateWorker(displayText, symbol, AddSymbolEncoding, contextPosition, sortText, insertionText, glyph, + filterText, matchPriority, supportedPlatforms, properties, rules); + } + + public static CompletionItem CreateWithNameAndKind( + string displayText, + IReadOnlyList symbols, + int contextPosition, + string sortText = null, + string insertionText = null, + Glyph? glyph = null, + string filterText = null, + int? matchPriority = null, + SupportedPlatformData supportedPlatforms = null, + ImmutableDictionary properties = null, + ImmutableArray tags = default(ImmutableArray), + CompletionItemRules rules = null) + { + return CreateWorker(displayText, symbols, AddSymbolNameAndKind, contextPosition, sortText, insertionText, glyph, + filterText, matchPriority, supportedPlatforms, properties, tags, rules); + } + + internal static string GetSymbolName(CompletionItem item) + { + if (item.Properties.TryGetValue("SymbolName", out var name)) + { + return name; + } + + return null; + } + + internal static SymbolKind? GetKind(CompletionItem item) + { + if (item.Properties.TryGetValue("SymbolKind", out var kind)) + { + return (SymbolKind)int.Parse(kind); + } + + return null; + } + + public static async Task GetDescriptionAsync(CompletionItem item, ImmutableArray symbols, Document document, SemanticModel semanticModel, CancellationToken cancellationToken) + { + var workspace = document.Project.Solution.Workspace; + + var position = SymbolCompletionItem.GetDescriptionPosition(item); + var supportedPlatforms = SymbolCompletionItem.GetSupportedPlatforms(item, workspace); + + var contextDocument = FindAppropriateDocumentForDescriptionContext(document, supportedPlatforms); + + if (symbols.Length != 0) + { + return await CommonCompletionUtilities.CreateDescriptionAsync(workspace, semanticModel, position, symbols, supportedPlatforms, cancellationToken).ConfigureAwait(false); + } + else + { + return CompletionDescription.Empty; + } + } } -} +} \ No newline at end of file diff --git a/src/Features/Core/Portable/Features.csproj b/src/Features/Core/Portable/Features.csproj index eb0f68d8aa077..20134d2ffc9e7 100644 --- a/src/Features/Core/Portable/Features.csproj +++ b/src/Features/Core/Portable/Features.csproj @@ -104,6 +104,7 @@ + diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/CompletionListTagCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/CompletionListTagCompletionProvider.vb index e0bc4fabf351b..dbef8a13f7396 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/CompletionListTagCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/CompletionListTagCompletionProvider.vb @@ -67,7 +67,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers End Function Protected Overrides Function CreateItem(displayText As String, insertionText As String, symbols As List(Of ISymbol), context As SyntaxContext, preselect As Boolean, supportedPlatformData As SupportedPlatformData) As CompletionItem - Return SymbolCompletionItem.Create( + Return SymbolCompletionItem.CreateWithSymbolId( displayText:=displayText, insertionText:=insertionText, filterText:=GetFilterText(symbols(0), displayText, context), diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/CrefCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/CrefCompletionProvider.vb index f7e3269fa8322..faaa33614f1ab 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/CrefCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/CrefCompletionProvider.vb @@ -9,10 +9,11 @@ Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Extensions.ContextQuery Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports System.Collections.Immutable Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers Partial Friend Class CrefCompletionProvider - Inherits CommonCompletionProvider + Inherits AbstractCrefCompletionProvider Private Shared ReadOnly s_crefFormat As SymbolDisplayFormat = New SymbolDisplayFormat( @@ -62,7 +63,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers Dim text = Await document.GetTextAsync(cancellationToken).ConfigureAwait(False) - Dim items = CreateCompletionItems(workspace, semanticModel, symbols, token.SpanStart) + Dim items = CreateCompletionItems(workspace, semanticModel, symbols, position) context.AddItems(items) If IsFirstCrefParameterContext(token) Then @@ -72,6 +73,28 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers context.IsExclusive = True End Function + Protected Overrides Async Function GetSymbolsAsync(document As Document, position As Integer, options As OptionSet, cancellationToken As CancellationToken) As Task(Of (SyntaxToken, SemanticModel, ImmutableArray(Of ISymbol))) + Dim tree = Await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(False) + Dim token = tree.GetTargetToken(position, cancellationToken) + + If IsCrefTypeParameterContext(token) Then + Return Nothing + End If + + ' To get a Speculative SemanticModel (which is much faster), we need to + ' walk up to the node the DocumentationTrivia is attached to. + Dim parentNode = token.Parent?.FirstAncestorOrSelf(Of DocumentationCommentTriviaSyntax)()?.ParentTrivia.Token.Parent + _testSpeculativeNodeCallbackOpt?.Invoke(parentNode) + If parentNode Is Nothing Then + Return Nothing + End If + + Dim semanticModel = Await document.GetSemanticModelForNodeAsync(parentNode, cancellationToken).ConfigureAwait(False) + Dim workspace = document.Project.Solution.Workspace + + Dim symbols = GetSymbols(token, semanticModel, cancellationToken) + Return (token, semanticModel, symbols.ToImmutableArray()) + End Function Private Shared Function IsCrefTypeParameterContext(token As SyntaxToken) As Boolean Return (token.IsChildToken(Function(t As TypeArgumentListSyntax) t.OfKeyword) OrElse @@ -114,7 +137,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers Return token.IsChildToken(Function(x As CrefSignatureSyntax) x.OpenParenToken) End Function - Private Shared Function GetSymbols(token As SyntaxToken, semanticModel As SemanticModel, cancellationToken As CancellationToken) As IEnumerable(Of ISymbol) + Private Overloads Shared Function GetSymbols(token As SyntaxToken, semanticModel As SemanticModel, cancellationToken As CancellationToken) As IEnumerable(Of ISymbol) If IsCrefStartContext(token) Then Return semanticModel.LookupSymbols(token.SpanStart) ElseIf IsCrefParameterListContext(token) Then @@ -197,22 +220,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers Dim displayString = builder.ToString() - Return SymbolCompletionItem.Create( + Return SymbolCompletionItem.CreateWithNameAndKind( displayText:=displayString, insertionText:=Nothing, - symbol:=symbol, + symbols:=ImmutableArray.Create(symbol), contextPosition:=position, rules:=GetRules(displayString)) End Function - Protected Overrides Function GetDescriptionWorkerAsync(document As Document, item As CompletionItem, cancellationToken As CancellationToken) As Task(Of CompletionDescription) - If CommonCompletionItem.HasDescription(item) Then - Return Task.FromResult(CommonCompletionItem.GetDescription(item)) - Else - Return SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken) - End If - End Function - Private Function CreateOfCompletionItem() As CompletionItem Return CommonCompletionItem.Create("Of", glyph:=Glyph.Keyword, description:=RecommendedKeyword.CreateDisplayParts("Of", VBFeaturesResources.Identifies_a_type_parameter_on_a_generic_class_structure_interface_delegate_or_procedure)) diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/EnumCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/EnumCompletionProvider.vb index 7264bd412dca4..46c1815111ed7 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/EnumCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/EnumCompletionProvider.vb @@ -123,7 +123,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers End Function Protected Overrides Function CreateItem(displayText As String, insertionText As String, symbols As List(Of ISymbol), context As SyntaxContext, preselect As Boolean, supportedPlatformData As SupportedPlatformData) As CompletionItem - Return SymbolCompletionItem.Create( + Return SymbolCompletionItem.CreateWithSymbolId( displayText:=displayText, insertionText:=insertionText, filterText:=GetFilterText(symbols(0), displayText, context), diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/HandlesClauseCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/HandlesClauseCompletionProvider.vb index cd65e273cdd46..460c837c43c41 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/HandlesClauseCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/HandlesClauseCompletionProvider.vb @@ -120,7 +120,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers Dim displayAndInsertionText = CompletionUtilities.GetDisplayAndInsertionText(symbol, context) - Return SymbolCompletionItem.Create( + Return SymbolCompletionItem.CreateWithSymbolId( displayText:=displayAndInsertionText.Item1, insertionText:=displayAndInsertionText.Item2, symbol:=symbol, diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/NamedParameterCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/NamedParameterCompletionProvider.vb index b203257879436..14c77360879a6 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/NamedParameterCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/NamedParameterCompletionProvider.vb @@ -66,7 +66,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers Dim text = Await document.GetTextAsync(cancellationToken).ConfigureAwait(False) For Each parameter In unspecifiedParameters - context.AddItem(SymbolCompletionItem.Create( + context.AddItem(SymbolCompletionItem.CreateWithSymbolId( displayText:=parameter.Name & s_colonEquals, insertionText:=parameter.Name.ToIdentifierToken().ToString() & s_colonEquals, symbol:=parameter,