From 3e5b27d9eecec93df4037cfe79bff93e346d81f9 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 18 May 2016 16:57:07 -0700 Subject: [PATCH 1/7] Don't preload completion providers we don't need. --- .../CompletionServiceWithProviders.cs | 72 +++++++++---------- 1 file changed, 34 insertions(+), 38 deletions(-) diff --git a/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs b/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs index 457c8b69490dd..31216f75ff9c3 100644 --- a/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs +++ b/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs @@ -29,6 +29,7 @@ public abstract class CompletionServiceWithProviders : CompletionService protected CompletionServiceWithProviders(Workspace workspace) { _workspace = workspace; + _createRoleProviders = CreateRoleProviders; } public override CompletionRules GetRules() @@ -68,7 +69,6 @@ private IEnumerable> GetImp internal void SetTestProviders(IEnumerable testProviders) { _testProviders = testProviders != null ? testProviders.ToImmutableArray() : ImmutableArray.Empty; - _lazyNameToProviderMap = null; } private class RoleProviders @@ -79,31 +79,36 @@ private class RoleProviders private readonly ConditionalWeakTable, RoleProviders> _roleProviders = new ConditionalWeakTable, RoleProviders>(); - protected ImmutableArray GetProviders(ImmutableHashSet roles) + private readonly ConditionalWeakTable, RoleProviders>.CreateValueCallback _createRoleProviders; + + private RoleProviders CreateRoleProviders(ImmutableHashSet roles) { - roles = roles ?? ImmutableHashSet.Empty; + var builtin = GetBuiltInProviders(); + var imported = GetImportedProviders() + .Where(lz => lz.Metadata.Roles == null || lz.Metadata.Roles.Length == 0 || roles.Overlaps(lz.Metadata.Roles)) + .Select(lz => lz.Value); - RoleProviders providers; - if (!_roleProviders.TryGetValue(roles, out providers)) + var providers = builtin.Concat(imported).Concat(_testProviders); + + lock (_gate) { - providers = _roleProviders.GetValue(roles, _ => + foreach (var provider in providers) { - var builtin = GetBuiltInProviders(); - var imported = GetImportedProviders() - .Where(lz => lz.Metadata.Roles == null || lz.Metadata.Roles.Length == 0 || roles.Overlaps(lz.Metadata.Roles)) - .Select(lz => lz.Value); - return new RoleProviders { Providers = builtin.Concat(imported).ToImmutableArray() }; - }); + _nameToProvider[provider.Name] = provider; + } } - if (_testProviders.Length > 0) - { - return providers.Providers.Concat(_testProviders); - } - else - { - return providers.Providers; - } + return new RoleProviders { Providers = providers.ToImmutableArray() }; + } + + protected ImmutableArray GetProviders(ImmutableHashSet roles) + { + roles = roles ?? ImmutableHashSet.Empty; + RoleProviders providers; + + return _roleProviders.TryGetValue(roles, out providers) + ? providers.Providers + : ImmutableArray.Empty; } protected virtual ImmutableArray GetProviders(ImmutableHashSet roles, CompletionTrigger trigger) @@ -118,19 +123,8 @@ protected virtual ImmutableArray GetProviders(ImmutableHashS } } - private ImmutableDictionary _lazyNameToProviderMap = null; - private ImmutableDictionary NameToProviderMap - { - get - { - if (_lazyNameToProviderMap == null) - { - Interlocked.CompareExchange(ref _lazyNameToProviderMap, CreateNameToProviderMap(), null); - } - - return _lazyNameToProviderMap; - } - } + private readonly object _gate = new object(); + private readonly Dictionary _nameToProvider = new Dictionary(); private ImmutableDictionary CreateNameToProviderMap() { @@ -150,15 +144,17 @@ private ImmutableDictionary CreateNameToProviderMap( internal protected CompletionProvider GetProvider(CompletionItem item) { string name; - CompletionProvider provider; + CompletionProvider provider = null; - if (item.Properties.TryGetValue("Provider", out name) - && this.NameToProviderMap.TryGetValue(name, out provider)) + if (item.Properties.TryGetValue("Provider", out name)) { - return provider; + lock (_gate) + { + _nameToProvider.TryGetValue(name, out provider); + } } - return null; + return provider; } public override async Task GetCompletionsAsync( From 167e4649b7595711a84e2ac5442ea2642b483907 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 18 May 2016 17:07:35 -0700 Subject: [PATCH 2/7] Move away from conditional weak tables. --- .../CompletionServiceWithProviders.cs | 64 +++++++------------ 1 file changed, 23 insertions(+), 41 deletions(-) diff --git a/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs b/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs index 31216f75ff9c3..a8ea338a091e6 100644 --- a/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs +++ b/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs @@ -23,9 +23,17 @@ namespace Microsoft.CodeAnalysis.Completion public abstract class CompletionServiceWithProviders : CompletionService { private static readonly Func> s_createList = _ => new List(); - private IEnumerable> _importedProviders; + + private readonly object _gate = new object(); + + private readonly Dictionary _nameToProvider = new Dictionary(); + private readonly Dictionary, ImmutableArray> _rolesToProviders = new Dictionary, ImmutableArray>(); + private readonly Func, ImmutableArray> _createRoleProviders; + private readonly Workspace _workspace; + private IEnumerable> _importedProviders; + protected CompletionServiceWithProviders(Workspace workspace) { _workspace = workspace; @@ -68,20 +76,15 @@ private IEnumerable> GetImp internal void SetTestProviders(IEnumerable testProviders) { - _testProviders = testProviders != null ? testProviders.ToImmutableArray() : ImmutableArray.Empty; - } - - private class RoleProviders - { - public ImmutableArray Providers; + lock (_gate) + { + _testProviders = testProviders != null ? testProviders.ToImmutableArray() : ImmutableArray.Empty; + _rolesToProviders.Clear(); + _nameToProvider.Clear(); + } } - private readonly ConditionalWeakTable, RoleProviders> _roleProviders - = new ConditionalWeakTable, RoleProviders>(); - - private readonly ConditionalWeakTable, RoleProviders>.CreateValueCallback _createRoleProviders; - - private RoleProviders CreateRoleProviders(ImmutableHashSet roles) + private ImmutableArray CreateRoleProviders(ImmutableHashSet roles) { var builtin = GetBuiltInProviders(); var imported = GetImportedProviders() @@ -90,25 +93,22 @@ private RoleProviders CreateRoleProviders(ImmutableHashSet roles) var providers = builtin.Concat(imported).Concat(_testProviders); - lock (_gate) + foreach (var provider in providers) { - foreach (var provider in providers) - { - _nameToProvider[provider.Name] = provider; - } + _nameToProvider[provider.Name] = provider; } - return new RoleProviders { Providers = providers.ToImmutableArray() }; + return providers.ToImmutableArray(); } protected ImmutableArray GetProviders(ImmutableHashSet roles) { roles = roles ?? ImmutableHashSet.Empty; - RoleProviders providers; - return _roleProviders.TryGetValue(roles, out providers) - ? providers.Providers - : ImmutableArray.Empty; + lock (_gate) + { + return _rolesToProviders.GetOrAdd(roles, _createRoleProviders); + } } protected virtual ImmutableArray GetProviders(ImmutableHashSet roles, CompletionTrigger trigger) @@ -123,24 +123,6 @@ protected virtual ImmutableArray GetProviders(ImmutableHashS } } - private readonly object _gate = new object(); - private readonly Dictionary _nameToProvider = new Dictionary(); - - private ImmutableDictionary CreateNameToProviderMap() - { - var map = ImmutableDictionary.Empty; - - foreach (var provider in GetBuiltInProviders().Concat(GetImportedProviders().Select(lz => lz.Value)).Concat(_testProviders)) - { - if (!map.ContainsKey(provider.Name)) - { - map = map.Add(provider.Name, provider); - } - } - - return map; - } - internal protected CompletionProvider GetProvider(CompletionItem item) { string name; From ca369ee8c0eb53f55f8957a853366b9cdda6f891 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 18 May 2016 17:17:09 -0700 Subject: [PATCH 3/7] Implement IEqualityComparer in case we get different role instances. --- .../CompletionServiceWithProviders.cs | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs b/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs index a8ea338a091e6..434d3d85c57cd 100644 --- a/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs +++ b/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs @@ -20,14 +20,14 @@ namespace Microsoft.CodeAnalysis.Completion /// /// A subtype of that aggregates completions from one or more s. /// - public abstract class CompletionServiceWithProviders : CompletionService + public abstract class CompletionServiceWithProviders : CompletionService, IEqualityComparer> { private static readonly Func> s_createList = _ => new List(); private readonly object _gate = new object(); private readonly Dictionary _nameToProvider = new Dictionary(); - private readonly Dictionary, ImmutableArray> _rolesToProviders = new Dictionary, ImmutableArray>(); + private readonly Dictionary, ImmutableArray> _rolesToProviders; private readonly Func, ImmutableArray> _createRoleProviders; private readonly Workspace _workspace; @@ -37,6 +37,7 @@ public abstract class CompletionServiceWithProviders : CompletionService protected CompletionServiceWithProviders(Workspace workspace) { _workspace = workspace; + _rolesToProviders = new Dictionary, ImmutableArray>(this); _createRoleProviders = CreateRoleProviders; } @@ -376,5 +377,39 @@ public override async Task GetChangeAsync(Document document, C return CompletionChange.Create(ImmutableArray.Create(new TextChange(item.Span, item.DisplayText))); } } + + bool IEqualityComparer>.Equals(ImmutableHashSet x, ImmutableHashSet y) + { + if (x == y) + { + return true; + } + + if (x.Count != y.Count) + { + return false; + } + + foreach (var v in x) + { + if (!y.Contains(v)) + { + return false; + } + } + + return true; + } + + int IEqualityComparer>.GetHashCode(ImmutableHashSet obj) + { + var hash = 0; + foreach (var o in obj) + { + hash += o.GetHashCode(); + } + + return hash; + } } } From ae8af1583ef0bb4bce5cce2350245474a6e27024 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 18 May 2016 18:49:06 -0700 Subject: [PATCH 4/7] Fix test code. --- .../AbstractCSharpCompletionProviderTests.cs | 17 +++- .../CrefCompletionProviderTests.cs | 3 +- ...bjectInitializerCompletionProviderTests.cs | 14 ++- .../OverrideCompletionProviderTests.cs | 19 ++-- .../SuggestionModeCompletionProviderTests.cs | 3 +- .../AbstractCompletionProviderTests.cs | 89 +++++++++---------- .../Test2/IntelliSense/TestState.vb | 2 +- ...tractVisualBasicCompletionProviderTests.vb | 17 +++- .../CrefCompletionProviderTests.vb | 4 +- ...ImplementsClauseCompletionProviderTests.vb | 5 +- ...bjectInitializerCompletionProviderTests.vb | 4 +- .../OverrideCompletionProviderTests.vb | 3 +- .../SuggestionModeCompletionProviderTests.vb | 3 +- .../Completion/CSharpCompletionService.cs | 5 +- .../Completion/CommonCompletionService.cs | 6 +- .../Portable/Completion/CompletionList.cs | 31 ++++++- .../CompletionServiceWithProviders.cs | 52 ++++++++--- .../VisualBasicCompletionService.vb | 5 +- .../Test/DebuggerIntelliSense/TestState.vb | 2 +- 19 files changed, 190 insertions(+), 94 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractCSharpCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractCSharpCompletionProviderTests.cs index f64bb00ec5080..3b3b159ea54cc 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractCSharpCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractCSharpCompletionProviderTests.cs @@ -1,6 +1,8 @@ // 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 System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion; @@ -20,6 +22,12 @@ protected AbstractCSharpCompletionProviderTests(CSharpTestWorkspaceFixture works { } + internal override CompletionServiceWithProviders CreateCompletionService( + TestWorkspace workspace, ImmutableArray exclusiveProviders) + { + return new CSharpCompletionService(workspace, exclusiveProviders); + } + protected override async Task VerifyWorkerAsync(string code, int position, string expectedItemOrNull, string expectedDescriptionOrNull, SourceCodeKind sourceCodeKind, bool usePreviousCharAsTrigger, bool checkForAbsence, bool experimental, int? glyph) { await VerifyAtPositionAsync(code, position, usePreviousCharAsTrigger, expectedItemOrNull, expectedDescriptionOrNull, sourceCodeKind, checkForAbsence, experimental, glyph); @@ -138,7 +146,8 @@ protected async Task VerifySendEnterThroughToEnterAsync(string initialMarkup, st var document = workspace.CurrentSolution.GetDocument(documentId); var position = hostDocument.CursorPosition.Value; - var completionList = await GetCompletionListAsync(document, position, CompletionTrigger.Default); + var service = await GetCompletionServiceAsync(); + var completionList = await GetCompletionListAsync(service, document, position, CompletionTrigger.Default); var item = completionList.Items.First(i => i.DisplayText.StartsWith(textTypedSoFar)); var optionService = workspace.Services.GetService(); @@ -166,7 +175,8 @@ private async Task VerifyTextualTriggerCharacterWorkerAsync(string markup, bool var options = workspace.Options.WithChangedOption(CompletionOptions.TriggerOnTypingLetters, LanguageNames.CSharp, triggerOnLetter); var trigger = CompletionTrigger.CreateInsertionTrigger(text[position]); - var isTextualTriggerCharacterResult = CompletionProvider.ShouldTriggerCompletion(text, position + 1, trigger, options); + var service = await GetCompletionServiceAsync(); + var isTextualTriggerCharacterResult = service.ShouldTriggerCompletion(text, position + 1, trigger, options: options); if (expectedTriggerCharacter) { @@ -205,7 +215,8 @@ protected async Task VerifyCommitCharactersAsync(string initialMarkup, string te var document = workspace.CurrentSolution.GetDocument(documentId); var position = hostDocument.CursorPosition.Value; - var completionList = await GetCompletionListAsync(document, position, CompletionTrigger.Default); + var service = await GetCompletionServiceAsync(); + var completionList = await GetCompletionListAsync(service, document, position, CompletionTrigger.Default); var item = completionList.Items.First(i => i.DisplayText.StartsWith(textTypedSoFar)); var completionRules = CompletionHelper.GetHelper(document); diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/CrefCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/CrefCompletionProviderTests.cs index fec8c5ee77426..b7e7d5d36ebad 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/CrefCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/CrefCompletionProviderTests.cs @@ -434,7 +434,8 @@ class C var provider = new CrefCompletionProvider(); var hostDocument = workspace.DocumentWithCursor; var document = workspace.CurrentSolution.GetDocument(hostDocument.Id); - var completionList = await GetCompletionListAsync(provider, document, hostDocument.CursorPosition.Value, CompletionTrigger.Default); + var service = await GetCompletionServiceAsync(); + var completionList = await GetCompletionListAsync(service, document, hostDocument.CursorPosition.Value, CompletionTrigger.Default); } } diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.cs index 8cd5559bdfd2a..692595df813a0 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.cs @@ -1,9 +1,12 @@ // 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 System; +using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion; +using Microsoft.CodeAnalysis.CSharp.Completion; using Microsoft.CodeAnalysis.CSharp.Completion.Providers; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Roslyn.Test.Utilities; @@ -17,6 +20,11 @@ public ObjectInitializerCompletionProviderTests(CSharpTestWorkspaceFixture works { } + internal override CompletionServiceWithProviders CreateCompletionService(TestWorkspace workspace, ImmutableArray exclusiveProviders) + { + return new CSharpCompletionService(workspace, exclusiveProviders); + } + internal override CompletionProvider CreateCompletionProvider() { return new ObjectInitializerCompletionProvider(); @@ -576,7 +584,8 @@ void foo() var document = workspace.CurrentSolution.GetDocument(hostDocument.Id); var triggerInfo = CompletionTrigger.CreateInsertionTrigger('a'); - var completionList = await GetCompletionListAsync(document, position, triggerInfo); + var service = await GetCompletionServiceAsync(); + var completionList = await GetCompletionListAsync(service, document, position, triggerInfo); var item = completionList.Items.First(); var completionRules = CompletionHelper.GetHelper(document); @@ -777,7 +786,8 @@ private async Task VerifyExclusiveAsync(string markup, bool exclusive) var document = workspace.CurrentSolution.GetDocument(hostDocument.Id); var triggerInfo = CompletionTrigger.CreateInsertionTrigger('a'); - var completionList = await GetCompletionListContextAsync(document, position, triggerInfo); + var service = await GetCompletionServiceAsync(); + var completionList = await GetCompletionListAsync(service, document, position, triggerInfo); if (completionList != null) { diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/OverrideCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/OverrideCompletionProviderTests.cs index caadb2e62ee55..d3f0c958ae262 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/OverrideCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/OverrideCompletionProviderTests.cs @@ -2126,10 +2126,11 @@ public override void set_Bar(int bay, int value) var document = solution.GetDocument(documentId); var triggerInfo = CompletionTrigger.Default; - var completionList = await GetCompletionListAsync(document, position, triggerInfo); + var service = await GetCompletionServiceAsync(); + var completionList = await GetCompletionListAsync(service, document, position, triggerInfo); var completionItem = completionList.Items.First(i => CompareItems(i.DisplayText, "Bar[int bay]")); - var customCommitCompletionProvider = CompletionProvider as ICustomCommitCompletionProvider; + var customCommitCompletionProvider = service.ExclusiveProviders?[0] as ICustomCommitCompletionProvider; if (customCommitCompletionProvider != null) { var textView = testWorkspace.GetTestDocument(documentId).GetTextView(); @@ -2385,10 +2386,11 @@ public override bool Equals(object obj) var document = solution.GetDocument(documentId); var triggerInfo = CompletionTrigger.Default; - var completionList = await GetCompletionListAsync(document, position, triggerInfo); + var service = await GetCompletionServiceAsync(); + var completionList = await GetCompletionListAsync(service, document, position, triggerInfo); var completionItem = completionList.Items.First(i => CompareItems(i.DisplayText, "Equals(object obj)")); - var customCommitCompletionProvider = CompletionProvider as ICustomCommitCompletionProvider; + var customCommitCompletionProvider = service.ExclusiveProviders?[0] as ICustomCommitCompletionProvider; if (customCommitCompletionProvider != null) { var textView = testWorkspace.GetTestDocument(documentId).GetTextView(); @@ -2443,10 +2445,11 @@ public override bool Equals(object obj) var document = solution.GetDocument(documentId); var triggerInfo = CompletionTrigger.Default; - var completionList = await GetCompletionListAsync(document, cursorPosition, triggerInfo); + var service = await GetCompletionServiceAsync(); + var completionList = await GetCompletionListAsync(service, document, cursorPosition, triggerInfo); var completionItem = completionList.Items.First(i => CompareItems(i.DisplayText, "Equals(object obj)")); - var customCommitCompletionProvider = CompletionProvider as ICustomCommitCompletionProvider; + var customCommitCompletionProvider = service.ExclusiveProviders?[0] as ICustomCommitCompletionProvider; if (customCommitCompletionProvider != null) { var textView = testWorkspace.GetTestDocument(documentId).GetTextView(); @@ -2545,7 +2548,9 @@ static void Main(string[] args) var provider = new OverrideCompletionProvider(); var testDocument = workspace.Documents.Single(); var document = workspace.CurrentSolution.GetDocument(testDocument.Id); - var completionList = await GetCompletionListAsync(provider, document, testDocument.CursorPosition.Value, CompletionTrigger.Default); + + var service = await GetCompletionServiceAsync(); + var completionList = await GetCompletionListAsync(service, document, testDocument.CursorPosition.Value, CompletionTrigger.Default); var oldTree = await document.GetSyntaxTreeAsync(); diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.cs index 21b16d334bc01..6369990d64ab4 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.cs @@ -580,7 +580,8 @@ private async Task VerifyWorkerAsync(string markup, bool isBuilder) private async Task CheckResultsAsync(Document document, int position, bool isBuilder) { var triggerInfo = CompletionTrigger.CreateInsertionTrigger('a'); - var completionList = await GetCompletionListAsync(document, position, triggerInfo); + var service = await GetCompletionServiceAsync(); + var completionList = await GetCompletionListAsync(service, document, position, triggerInfo); if (isBuilder) { diff --git a/src/EditorFeatures/Test/Completion/AbstractCompletionProviderTests.cs b/src/EditorFeatures/Test/Completion/AbstractCompletionProviderTests.cs index 381c10db4dd9f..4d44b10a50541 100644 --- a/src/EditorFeatures/Test/Completion/AbstractCompletionProviderTests.cs +++ b/src/EditorFeatures/Test/Completion/AbstractCompletionProviderTests.cs @@ -28,7 +28,6 @@ public abstract class AbstractCompletionProviderTests : TestB where TWorkspaceFixture : TestWorkspaceFixture, new() { protected readonly Mock MockCompletionSession; - internal CompletionProvider CompletionProvider; protected TWorkspaceFixture WorkspaceFixture; protected AbstractCompletionProviderTests(TWorkspaceFixture workspaceFixture) @@ -36,7 +35,6 @@ protected AbstractCompletionProviderTests(TWorkspaceFixture workspaceFixture) MockCompletionSession = new Mock(MockBehavior.Strict); this.WorkspaceFixture = workspaceFixture; - this.CompletionProvider = CreateCompletionProvider(); } public override void Dispose() @@ -53,20 +51,29 @@ protected static async Task CanUseSpeculativeSemanticModelAsync(Document d return !service.GetMemberBodySpanForSpeculativeBinding(node).IsEmpty; } - internal static CompletionService GetCompletionService(Document document) + internal async Task GetCompletionServiceAsync() { - return CompletionService.GetService(document); + var workspace = await this.WorkspaceFixture.GetWorkspaceAsync(); + return CreateCompletionService(workspace, ImmutableArray.Create(CreateCompletionProvider())); } + internal abstract CompletionServiceWithProviders CreateCompletionService( + TestWorkspace workspace, ImmutableArray exclusiveProviders); + internal static CompletionHelper GetCompletionHelper(Document document) { return CompletionHelper.GetHelper(document); } - internal static async Task GetCompletionListContextAsync(CompletionProvider provider, Document document, int position, CompletionTrigger triggerInfo, OptionSet options = null) + internal static async Task GetCompletionListContextAsync( + CompletionService service, + CompletionProvider provider, + Document document, + int position, + CompletionTrigger triggerInfo, + OptionSet options = null) { options = options ?? document.Project.Solution.Workspace.Options; - var service = document.Project.LanguageServices.GetService(); var text = await document.GetTextAsync(); var span = service.GetDefaultItemSpan(text, position); var context = new CompletionContext(provider, document, position, span, triggerInfo, options, CancellationToken.None); @@ -74,26 +81,15 @@ internal static async Task GetCompletionListContextAsync(Comp return context; } - internal Task GetCompletionListContextAsync(Document document, int position, CompletionTrigger triggerInfo, OptionSet options = null) - { - return GetCompletionListContextAsync(this.CompletionProvider, document, position, triggerInfo, options); - } - - internal static async Task GetCompletionListAsync(CompletionProvider provider, Document document, int position, CompletionTrigger triggerInfo, OptionSet options = null) - { - var service = GetCompletionService(document); - var context = await GetCompletionListContextAsync(provider, document, position, triggerInfo, options); - var text = await document.GetTextAsync(); - var span = service.GetDefaultItemSpan(text, position); - return CompletionList.Create(span, context.Items.ToImmutableArray(), service.GetRules(), context.SuggestionModeItem); - } - - internal Task GetCompletionListAsync(Document document, int position, CompletionTrigger triggerInfo, OptionSet options = null) + internal Task GetCompletionListAsync( + CompletionService service, + Document document, int position, CompletionTrigger triggerInfo, OptionSet options = null) { - return GetCompletionListAsync(this.CompletionProvider, document, position, triggerInfo, options); + return service.GetCompletionsAsync(document, position, triggerInfo, options: options); } - private async Task CheckResultsAsync(Document document, int position, string expectedItemOrNull, string expectedDescriptionOrNull, bool usePreviousCharAsTrigger, bool checkForAbsence, Glyph? glyph) + private async Task CheckResultsAsync( + Document document, int position, string expectedItemOrNull, string expectedDescriptionOrNull, bool usePreviousCharAsTrigger, bool checkForAbsence, Glyph? glyph) { var code = (await document.GetTextAsync()).ToString(); @@ -104,8 +100,8 @@ private async Task CheckResultsAsync(Document document, int position, string exp trigger = CompletionTrigger.CreateInsertionTrigger(insertedCharacter: code.ElementAt(position - 1)); } - var completionList = await GetCompletionListAsync(document, position, trigger); - var completionService = document.Project.LanguageServices.GetService(); + var completionService = await GetCompletionServiceAsync(); + var completionList = await GetCompletionListAsync(completionService, document, position, trigger); var items = completionList == null ? default(ImmutableArray) : completionList.Items; if (checkForAbsence) @@ -308,10 +304,11 @@ private async Task VerifyCustomCommitProviderCheckResultsAsync(Document document { var textBuffer = (await WorkspaceFixture.GetWorkspaceAsync()).Documents.Single().TextBuffer; - var items = (await GetCompletionListAsync(document, position, CompletionTrigger.Default)).Items; + var service = await GetCompletionServiceAsync(); + var items = (await GetCompletionListAsync(service, document, position, CompletionTrigger.Default)).Items; var firstItem = items.First(i => CompareItems(i.DisplayText, itemToCommit)); - var customCommitCompletionProvider = CompletionProvider as ICustomCommitCompletionProvider; + var customCommitCompletionProvider = service.ExclusiveProviders?[0] as ICustomCommitCompletionProvider; if (customCommitCompletionProvider != null) { var completionRules = GetCompletionHelper(document); @@ -320,11 +317,12 @@ private async Task VerifyCustomCommitProviderCheckResultsAsync(Document document } else { - await VerifyCustomCommitWorkerAsync(document, firstItem, codeBeforeCommit, expectedCodeAfterCommit, commitChar); + await VerifyCustomCommitWorkerAsync(service, document, firstItem, codeBeforeCommit, expectedCodeAfterCommit, commitChar); } } - internal virtual async Task VerifyCustomCommitWorkerAsync( + internal async Task VerifyCustomCommitWorkerAsync( + CompletionServiceWithProviders service, Document document, CompletionItem completionItem, string codeBeforeCommit, @@ -343,10 +341,9 @@ internal virtual async Task VerifyCustomCommitWorkerAsync( return; } - var service = CompletionService.GetService(document); - var commit = service.GetChangeAsync(document, completionItem, commitChar).Result; + var commit = await service.GetChangeAsync(document, completionItem, commitChar, CancellationToken.None); - var text = document.GetTextAsync().Result; + var text = await document.GetTextAsync(); var newText = text.WithChanges(commit.TextChanges); var newDoc = document.WithText(newText); document.Project.Solution.Workspace.TryApplyChanges(newDoc.Project.Solution); @@ -415,7 +412,8 @@ private async Task VerifyProviderCommitCheckResultsAsync(Document document, int var textBuffer = (await WorkspaceFixture.GetWorkspaceAsync()).Documents.Single().TextBuffer; var textSnapshot = textBuffer.CurrentSnapshot.AsText(); - var items = (await GetCompletionListAsync(document, position, CompletionTrigger.Default)).Items; + var service = await GetCompletionServiceAsync(); + var items = (await GetCompletionListAsync(service, document, position, CompletionTrigger.Default)).Items; var firstItem = items.First(i => CompareItems(i.DisplayText, itemToCommit)); var completionRules = GetCompletionHelper(document); @@ -445,11 +443,10 @@ private async Task VerifyProviderCommitCheckResultsAsync(Document document, int Assert.Equal(expectedCodeAfterCommit, text.ToString()); } - protected async Task VerifyItemInEditorBrowsableContextsAsync(string markup, string referencedCode, string item, int expectedSymbolsSameSolution, int expectedSymbolsMetadataReference, - string sourceLanguage, string referencedLanguage, bool hideAdvancedMembers = false) + protected async Task VerifyItemInEditorBrowsableContextsAsync( + string markup, string referencedCode, string item, int expectedSymbolsSameSolution, int expectedSymbolsMetadataReference, + string sourceLanguage, string referencedLanguage, bool hideAdvancedMembers = false) { - CompletionProvider = CreateCompletionProvider(); - await VerifyItemWithMetadataReferenceAsync(markup, referencedCode, item, expectedSymbolsMetadataReference, sourceLanguage, referencedLanguage, hideAdvancedMembers); await VerifyItemWithProjectReferenceAsync(markup, referencedCode, item, expectedSymbolsSameSolution, sourceLanguage, referencedLanguage, hideAdvancedMembers); @@ -538,7 +535,8 @@ private Task VerifyItemInSameProjectAsync(string markup, string referencedCode, return VerifyItemWithReferenceWorkerAsync(xmlString, expectedItem, expectedSymbols, hideAdvancedMembers); } - private async Task VerifyItemWithReferenceWorkerAsync(string xmlString, string expectedItem, int expectedSymbols, bool hideAdvancedMembers) + private async Task VerifyItemWithReferenceWorkerAsync( + string xmlString, string expectedItem, int expectedSymbols, bool hideAdvancedMembers) { using (var testWorkspace = await TestWorkspace.CreateAsync(xmlString)) { @@ -552,8 +550,8 @@ private async Task VerifyItemWithReferenceWorkerAsync(string xmlString, string e var triggerInfo = CompletionTrigger.Default; - var completionList = await GetCompletionListAsync(document, position, triggerInfo); - var completionService = document.Project.LanguageServices.GetService(); + var completionService = await GetCompletionServiceAsync(); + var completionList = await GetCompletionListAsync(completionService, document, position, triggerInfo); if (expectedSymbols >= 1) { @@ -596,7 +594,8 @@ protected Task VerifyItemWithMscorlib45Async(string markup, string expectedItem, return VerifyItemWithMscorlib45WorkerAsync(xmlString, expectedItem, expectedDescription); } - private async Task VerifyItemWithMscorlib45WorkerAsync(string xmlString, string expectedItem, string expectedDescription) + private async Task VerifyItemWithMscorlib45WorkerAsync( + string xmlString, string expectedItem, string expectedDescription) { using (var testWorkspace = await TestWorkspace.CreateAsync(xmlString)) { @@ -606,8 +605,8 @@ private async Task VerifyItemWithMscorlib45WorkerAsync(string xmlString, string var document = solution.GetDocument(documentId); var triggerInfo = CompletionTrigger.Default; - var completionList = await GetCompletionListAsync(document, position, triggerInfo); - var completionService = document.Project.LanguageServices.GetService(); + var completionService = await GetCompletionServiceAsync(); + var completionList = await GetCompletionListAsync(completionService, document, position, triggerInfo); var item = completionList.Items.FirstOrDefault(i => i.DisplayText == expectedItem); Assert.Equal(expectedDescription, (await completionService.GetDescriptionAsync(document, item)).Text); @@ -638,8 +637,8 @@ protected async Task VerifyItemInLinkedFilesAsync(string xmlString, string expec var document = solution.GetDocument(currentContextDocumentId); var triggerInfo = CompletionTrigger.Default; - var completionList = await GetCompletionListAsync(document, position, triggerInfo); - var completionService = document.Project.LanguageServices.GetService(); + var completionService = await GetCompletionServiceAsync(); + var completionList = await GetCompletionListAsync(completionService, document, position, triggerInfo); var item = completionList.Items.Single(c => c.DisplayText == expectedItem); Assert.NotNull(item); diff --git a/src/EditorFeatures/Test2/IntelliSense/TestState.vb b/src/EditorFeatures/Test2/IntelliSense/TestState.vb index 8f0457712c27d..cadacdf933159 100644 --- a/src/EditorFeatures/Test2/IntelliSense/TestState.vb +++ b/src/EditorFeatures/Test2/IntelliSense/TestState.vb @@ -52,7 +52,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Dim languageServices = Me.Workspace.CurrentSolution.Projects.First().LanguageServices Dim language = languageServices.Language - If (extraCompletionProviders IsNot Nothing) Then + If extraCompletionProviders IsNot Nothing Then Dim completionService = DirectCast(languageServices.GetService(Of CompletionService), CompletionServiceWithProviders) completionService.SetTestProviders(extraCompletionProviders.Select(Function(lz) lz.Value).ToList()) End If diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AbstractVisualBasicCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AbstractVisualBasicCompletionProviderTests.vb index e2a8e1c2a0ae0..a1289ce9c3186 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AbstractVisualBasicCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AbstractVisualBasicCompletionProviderTests.vb @@ -1,10 +1,12 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +Imports System.Collections.Immutable Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.Completion Imports Microsoft.CodeAnalysis.Editor.UnitTests.Completion Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.Text +Imports Microsoft.CodeAnalysis.VisualBasic.Completion Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.CompletionProviders Public MustInherit Class AbstractVisualBasicCompletionProviderTests @@ -14,6 +16,10 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet MyBase.New(workspaceFixture) End Sub + Friend Overrides Function CreateCompletionService(workspace As TestWorkspace, exclusiveProviders As ImmutableArray(Of CompletionProvider)) As CompletionServiceWithProviders + Return New VisualBasicCompletionService(workspace, exclusiveProviders) + End Function + Protected Overrides Async Function VerifyWorkerAsync(code As String, position As Integer, expectedItemOrNull As String, expectedDescriptionOrNull As String, sourceCodeKind As SourceCodeKind, usePreviousCharAsTrigger As Boolean, checkForAbsence As Boolean, experimental As Boolean, glyph As Integer?) As Threading.Tasks.Task ' Script/interactive support removed for now. ' TODO: Re-enable these when interactive is back in the product. @@ -111,7 +117,8 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet Dim document = workspace.CurrentSolution.GetDocument(documentId) Dim position = hostDocument.CursorPosition.Value - Dim completionList = Await GetCompletionListAsync(document, position, CompletionTrigger.Default) + Dim service = Await GetCompletionServiceAsync() + Dim completionList = Await GetCompletionListAsync(service, document, position, CompletionTrigger.Default) Dim item = completionList.Items.First(Function(i) i.DisplayText.StartsWith(textTypedSoFar)) Dim helper = CompletionHelper.GetHelper(document) @@ -131,7 +138,8 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet Dim document = workspace.CurrentSolution.GetDocument(documentId) Dim position = hostDocument.CursorPosition.Value - Dim completionList = Await GetCompletionListAsync(document, position, CompletionTrigger.Default) + Dim service = Await GetCompletionServiceAsync() + Dim completionList = Await GetCompletionListAsync(service, document, position, CompletionTrigger.Default) Dim item = completionList.Items.First() Dim helper = CompletionHelper.GetHelper(document) @@ -197,7 +205,10 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet Dim text = document.TextBuffer.CurrentSnapshot.AsText() Dim options = workspace.Options.WithChangedOption(CompletionOptions.TriggerOnTypingLetters, LanguageNames.VisualBasic, triggerOnLetter) Dim trigger = CompletionTrigger.CreateInsertionTrigger(text(position)) - Dim isTextualTriggerCharacterResult = CompletionProvider.ShouldTriggerCompletion(text, position + 1, trigger, options) + + Dim completionService = Await GetCompletionServiceAsync() + Dim isTextualTriggerCharacterResult = completionService.ShouldTriggerCompletion( + text, position + 1, trigger, options:=options) If expectedTriggerCharacter Then Dim assertText = "'" & text.ToString(New TextSpan(position, 1)) & "' expected to be textual trigger character" diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/CrefCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/CrefCompletionProviderTests.vb index a0feab3b63da5..3533f5f8e3d9b 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/CrefCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/CrefCompletionProviderTests.vb @@ -429,10 +429,10 @@ End Class]]>.Value.NormalizeLineEndings() ' This verifies that the provider is asking for a speculative SemanticModel ' by walking to the node the documentation is attached to. - Dim provider = New CrefCompletionProvider() Dim hostDocument = workspace.DocumentWithCursor Dim document = workspace.CurrentSolution.GetDocument(hostDocument.Id) - Dim completionList = Await GetCompletionListAsync(provider, document, hostDocument.CursorPosition.Value, CompletionTrigger.Default) + Dim service = Await GetCompletionServiceAsync() + Dim completionList = Await GetCompletionListAsync(service, document, hostDocument.CursorPosition.Value, CompletionTrigger.Default) End Using End Function diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ImplementsClauseCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ImplementsClauseCompletionProviderTests.vb index 510ed86253dda..532886f058ca4 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ImplementsClauseCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ImplementsClauseCompletionProviderTests.vb @@ -643,10 +643,9 @@ End Interface Using workspace = Await TestWorkspace.CreateAsync(element) Dim position = workspace.Documents.Single().CursorPosition.Value Dim document = workspace.CurrentSolution.GetDocument(workspace.Documents.Single().Id) - - Dim completionList = Await GetCompletionListAsync(document, position, CompletionTrigger.Default) + Dim service = Await GetCompletionServiceAsync() + Dim completionList = Await GetCompletionListAsync(service, document, position, CompletionTrigger.Default) AssertEx.Any(completionList.Items, Function(c) c.DisplayText = "Workcover") - End Using End Function diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.vb index 6a279ceb071ac..c2300cd48a83e 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.vb @@ -406,8 +406,8 @@ End Program Dim hostDocument = workspace.Documents.First() Dim caretPosition = hostDocument.CursorPosition.Value Dim document = workspace.CurrentSolution.GetDocument(hostDocument.Id) - - Dim completionList = Await GetCompletionListContextAsync(document, caretPosition, CompletionTrigger.Default) + Dim service = Await GetCompletionServiceAsync() + Dim completionList = Await GetCompletionListAsync(service, document, caretPosition, CompletionTrigger.Default) Assert.True(completionList Is Nothing OrElse completionList.IsExclusive, "Expected always exclusive") End Using End Function diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/OverrideCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/OverrideCompletionProviderTests.vb index 8c1a3787a77e1..9ac0465867d9f 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/OverrideCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/OverrideCompletionProviderTests.vb @@ -1746,7 +1746,8 @@ public class C Dim caretPosition = hostDocument.CursorPosition.Value Dim document = workspace.CurrentSolution.GetDocument(hostDocument.Id) - Dim completionList = Await GetCompletionListAsync(document, caretPosition, CompletionTrigger.Default) + Dim service = Await GetCompletionServiceAsync() + Dim completionList = Await GetCompletionListAsync(service, document, caretPosition, CompletionTrigger.Default) Assert.False(completionList.Items.Any(Function(c) c.DisplayText = "e")) End Using End Function diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.vb index 100b9562e6450..78b8c3b5f5dca 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.vb @@ -334,7 +334,8 @@ End Class Private Async Function CheckResultsAsync(document As Document, position As Integer, isBuilder As Boolean, triggerInfo As CompletionTrigger?, options As OptionSet) As Task triggerInfo = If(triggerInfo, CompletionTrigger.CreateInsertionTrigger("a"c)) - Dim completionList = Await GetCompletionListAsync(document, position, triggerInfo.Value, options) + Dim service = Await GetCompletionServiceAsync() + Dim completionList = Await GetCompletionListAsync(service, document, position, triggerInfo.Value, options) If isBuilder Then Assert.NotNull(completionList) diff --git a/src/Features/CSharp/Portable/Completion/CSharpCompletionService.cs b/src/Features/CSharp/Portable/Completion/CSharpCompletionService.cs index 32850090de92e..7db3c864ebb40 100644 --- a/src/Features/CSharp/Portable/Completion/CSharpCompletionService.cs +++ b/src/Features/CSharp/Portable/Completion/CSharpCompletionService.cs @@ -50,8 +50,9 @@ internal class CSharpCompletionService : CommonCompletionService private readonly Workspace _workspace; - public CSharpCompletionService(Workspace workspace) - : base(workspace) + public CSharpCompletionService( + Workspace workspace, ImmutableArray? exclusiveProviders = null) + : base(workspace, exclusiveProviders) { _workspace = workspace; } diff --git a/src/Features/Core/Portable/Completion/CommonCompletionService.cs b/src/Features/Core/Portable/Completion/CommonCompletionService.cs index 1e7be956cfe6a..ea20236f5f7b9 100644 --- a/src/Features/Core/Portable/Completion/CommonCompletionService.cs +++ b/src/Features/Core/Portable/Completion/CommonCompletionService.cs @@ -20,8 +20,10 @@ namespace Microsoft.CodeAnalysis.Completion { internal abstract partial class CommonCompletionService : CompletionServiceWithProviders { - protected CommonCompletionService(Workspace workspace) - : base(workspace) + protected CommonCompletionService( + Workspace workspace, + ImmutableArray? exclusiveProviders) + : base(workspace, exclusiveProviders) { } diff --git a/src/Features/Core/Portable/Completion/CompletionList.cs b/src/Features/Core/Portable/Completion/CompletionList.cs index 903e787a39148..3484135463e62 100644 --- a/src/Features/Core/Portable/Completion/CompletionList.cs +++ b/src/Features/Core/Portable/Completion/CompletionList.cs @@ -36,12 +36,23 @@ public sealed class CompletionList /// public CompletionItem SuggestionModeItem { get; } - private CompletionList(TextSpan defaultSpan, ImmutableArray items, CompletionRules rules, CompletionItem suggestionModeItem) + /// + /// For testing purposes only. + /// + internal bool IsExclusive { get; } + + private CompletionList( + TextSpan defaultSpan, + ImmutableArray items, + CompletionRules rules, + CompletionItem suggestionModeItem, + bool isExclusive) { this.DefaultSpan = defaultSpan; this.Items = items.IsDefault ? ImmutableArray.Empty : items; this.Rules = rules ?? CompletionRules.Default; this.SuggestionModeItem = suggestionModeItem; + this.IsExclusive = isExclusive; } /// @@ -58,7 +69,18 @@ public static CompletionList Create( CompletionRules rules = null, CompletionItem suggestionModeItem = null) { - return new CompletionList(defaultSpan, FixItemSpans(items, defaultSpan), rules, suggestionModeItem); + return Create(defaultSpan, items, rules, suggestionModeItem, isExclusive: false); + } + + internal static CompletionList Create( + TextSpan defaultSpan, + ImmutableArray items, + CompletionRules rules, + CompletionItem suggestionModeItem, + bool isExclusive) + { + return new CompletionList( + defaultSpan, FixItemSpans(items, defaultSpan), rules, suggestionModeItem, isExclusive); } private static ImmutableArray FixItemSpans(ImmutableArray items, TextSpan defaultSpan) @@ -130,7 +152,8 @@ public CompletionList WithSuggestionModeItem(CompletionItem suggestionModeItem) /// /// The default returned when no items are found to populate the list. /// - public static readonly CompletionList Empty - = new CompletionList(default(TextSpan), default(ImmutableArray), CompletionRules.Default, null); + public static readonly CompletionList Empty = new CompletionList( + default(TextSpan), default(ImmutableArray), CompletionRules.Default, + suggestionModeItem: null, isExclusive: false); } } diff --git a/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs b/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs index 434d3d85c57cd..dfb1065b8a4b2 100644 --- a/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs +++ b/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs @@ -32,11 +32,24 @@ public abstract class CompletionServiceWithProviders : CompletionService, IEqual private readonly Workspace _workspace; + /// + /// Internal for testing purposes. + /// + internal readonly ImmutableArray? ExclusiveProviders; + private IEnumerable> _importedProviders; protected CompletionServiceWithProviders(Workspace workspace) + : this(workspace, null) + { + } + + internal CompletionServiceWithProviders( + Workspace workspace, + ImmutableArray? exclusiveProviders = null) { _workspace = workspace; + ExclusiveProviders = exclusiveProviders; _rolesToProviders = new Dictionary, ImmutableArray>(this); _createRoleProviders = CreateRoleProviders; } @@ -74,6 +87,7 @@ private IEnumerable> GetImp } private ImmutableArray _testProviders = ImmutableArray.Empty; + private object p; internal void SetTestProviders(IEnumerable testProviders) { @@ -87,18 +101,29 @@ internal void SetTestProviders(IEnumerable testProviders) private ImmutableArray CreateRoleProviders(ImmutableHashSet roles) { - var builtin = GetBuiltInProviders(); - var imported = GetImportedProviders() - .Where(lz => lz.Metadata.Roles == null || lz.Metadata.Roles.Length == 0 || roles.Overlaps(lz.Metadata.Roles)) - .Select(lz => lz.Value); - - var providers = builtin.Concat(imported).Concat(_testProviders); + var providers = GetAllProviders(roles); foreach (var provider in providers) { _nameToProvider[provider.Name] = provider; } + return providers; + } + + private ImmutableArray GetAllProviders(ImmutableHashSet roles) + { + if (ExclusiveProviders.HasValue) + { + return ExclusiveProviders.Value; + } + + var builtin = GetBuiltInProviders(); + var imported = GetImportedProviders() + .Where(lz => lz.Metadata.Roles == null || lz.Metadata.Roles.Length == 0 || roles.Overlaps(lz.Metadata.Roles)) + .Select(lz => lz.Value); + + var providers = builtin.Concat(imported).Concat(_testProviders); return providers.ToImmutableArray(); } @@ -193,7 +218,9 @@ public override async Task GetCompletionsAsync( if (firstExclusiveList != null) { - return MergeAndPruneCompletionLists(SpecializedCollections.SingletonEnumerable(firstExclusiveList), defaultItemSpan); + return MergeAndPruneCompletionLists( + SpecializedCollections.SingletonEnumerable(firstExclusiveList), defaultItemSpan, + isExclusive: true); } // If no exclusive providers provided anything, then go through the remaining @@ -230,10 +257,11 @@ public override async Task GetCompletionsAsync( // groups are properly ordered based on the original providers. allProvidersAndLists.Sort((p1, p2) => completionProviderToIndex[p1.Provider] - completionProviderToIndex[p2.Provider]); - return MergeAndPruneCompletionLists(allProvidersAndLists, defaultItemSpan); + return MergeAndPruneCompletionLists(allProvidersAndLists, defaultItemSpan, isExclusive: false); } - private CompletionList MergeAndPruneCompletionLists(IEnumerable completionLists, TextSpan contextSpan) + private CompletionList MergeAndPruneCompletionLists( + IEnumerable completionLists, TextSpan contextSpan, bool isExclusive) { var displayNameToItemsMap = new Dictionary>(); CompletionItem suggestionModeItem = null; @@ -261,7 +289,8 @@ private CompletionList MergeAndPruneCompletionLists(IEnumerable p.ShouldTriggerCompletion(text, caretPosition, trigger, options)); } - public override async Task GetChangeAsync(Document document, CompletionItem item, char? commitKey, CancellationToken cancellationToken) + public override async Task GetChangeAsync( + Document document, CompletionItem item, char? commitKey, CancellationToken cancellationToken) { var provider = GetProvider(item); if (provider != null) diff --git a/src/Features/VisualBasic/Portable/Completion/VisualBasicCompletionService.vb b/src/Features/VisualBasic/Portable/Completion/VisualBasicCompletionService.vb index caaecdc830a80..54620ed7486fe 100644 --- a/src/Features/VisualBasic/Portable/Completion/VisualBasicCompletionService.vb +++ b/src/Features/VisualBasic/Portable/Completion/VisualBasicCompletionService.vb @@ -45,8 +45,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion Private ReadOnly _workspace As Workspace - Public Sub New(workspace As Workspace) - MyBase.New(workspace) + Public Sub New(workspace As Workspace, + Optional exclusiveProviders As ImmutableArray(Of CompletionProvider) ? = Nothing) + MyBase.New(workspace, exclusiveProviders) _workspace = workspace End Sub diff --git a/src/VisualStudio/Core/Test/DebuggerIntelliSense/TestState.vb b/src/VisualStudio/Core/Test/DebuggerIntelliSense/TestState.vb index 70c30b08a44a3..7142961d70ddd 100644 --- a/src/VisualStudio/Core/Test/DebuggerIntelliSense/TestState.vb +++ b/src/VisualStudio/Core/Test/DebuggerIntelliSense/TestState.vb @@ -57,7 +57,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.DebuggerIntelliSense Dim languageServices = Me.Workspace.CurrentSolution.Projects.First().LanguageServices Dim language = languageServices.Language - If (extraCompletionProviders IsNot Nothing) Then + If extraCompletionProviders IsNot Nothing Then Dim completionService = DirectCast(languageServices.GetService(Of CompletionService), CommonCompletionService) completionService.SetTestProviders(extraCompletionProviders.Select(Function(lz) lz.Value).ToList()) End If From 6f2c49497683f29f1629a97a6b263f20626c9e13 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 18 May 2016 19:19:11 -0700 Subject: [PATCH 5/7] Fixup completion tests to properly call through the CompletionService. --- .../AbstractCSharpCompletionProviderTests.cs | 21 +++++------ .../CrefCompletionProviderTests.cs | 2 +- ...bjectInitializerCompletionProviderTests.cs | 15 ++------ .../OverrideCompletionProviderTests.cs | 32 ++++++++-------- .../SuggestionModeCompletionProviderTests.cs | 6 ++- .../Completion/CompletionHelper.cs | 15 ++++---- .../Completion/CompletionHelperFactory.cs | 3 +- .../Completion/Controller_ReturnKey.cs | 2 - .../Completion/Controller_TypeChar.cs | 4 +- .../DescriptionModifyingPresentationItem.cs | 2 +- .../Presentation/RoslynCompletionSet.cs | 4 +- .../AbstractCompletionProviderTests.cs | 35 +++++++++--------- .../IntelliSense/CompletionRulesTests.vb | 8 +++- .../Completion/VisualBasicCompletionHelper.vb | 12 ++---- ...tractVisualBasicCompletionProviderTests.vb | 14 +++---- .../CrefCompletionProviderTests.vb | 2 +- ...ImplementsClauseCompletionProviderTests.vb | 2 +- ...bjectInitializerCompletionProviderTests.vb | 2 +- .../OverrideCompletionProviderTests.vb | 2 +- .../SuggestionModeCompletionProviderTests.vb | 15 ++++---- .../XmlDocCommentCompletionProviderTests.vb | 10 ++++- .../CSharpSuggestionModeCompletionProvider.cs | 1 + .../CompletionServiceWithProviders.cs | 37 ++++++++++++++++--- ...actReferenceDirectiveCompletionProvider.cs | 3 +- 24 files changed, 140 insertions(+), 109 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractCSharpCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractCSharpCompletionProviderTests.cs index 3b3b159ea54cc..379668ac18f33 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractCSharpCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractCSharpCompletionProviderTests.cs @@ -1,6 +1,5 @@ // 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 System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -10,7 +9,6 @@ using Microsoft.CodeAnalysis.CSharp.Completion; using Microsoft.CodeAnalysis.Editor.UnitTests.Completion; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; using Xunit; @@ -23,7 +21,7 @@ protected AbstractCSharpCompletionProviderTests(CSharpTestWorkspaceFixture works } internal override CompletionServiceWithProviders CreateCompletionService( - TestWorkspace workspace, ImmutableArray exclusiveProviders) + Workspace workspace, ImmutableArray exclusiveProviders) { return new CSharpCompletionService(workspace, exclusiveProviders); } @@ -146,15 +144,14 @@ protected async Task VerifySendEnterThroughToEnterAsync(string initialMarkup, st var document = workspace.CurrentSolution.GetDocument(documentId); var position = hostDocument.CursorPosition.Value; - var service = await GetCompletionServiceAsync(); + workspace.Options = workspace.Options.WithChangedOption( + CSharpCompletionOptions.AddNewLineOnEnterAfterFullyTypedWord, sendThroughEnterEnabled); + + var service = GetCompletionService(workspace); var completionList = await GetCompletionListAsync(service, document, position, CompletionTrigger.Default); var item = completionList.Items.First(i => i.DisplayText.StartsWith(textTypedSoFar)); - var optionService = workspace.Services.GetService(); - var options = optionService.GetOptions().WithChangedOption(CSharpCompletionOptions.AddNewLineOnEnterAfterFullyTypedWord, sendThroughEnterEnabled); - optionService.SetOptions(options); - - var completionRules = CompletionHelper.GetHelper(document); + var completionRules = CompletionHelper.GetHelper(document, service); Assert.Equal(expected, completionRules.SendEnterThroughToEditor(item, textTypedSoFar, workspace.Options)); } } @@ -175,7 +172,7 @@ private async Task VerifyTextualTriggerCharacterWorkerAsync(string markup, bool var options = workspace.Options.WithChangedOption(CompletionOptions.TriggerOnTypingLetters, LanguageNames.CSharp, triggerOnLetter); var trigger = CompletionTrigger.CreateInsertionTrigger(text[position]); - var service = await GetCompletionServiceAsync(); + var service = GetCompletionService(workspace); var isTextualTriggerCharacterResult = service.ShouldTriggerCompletion(text, position + 1, trigger, options: options); if (expectedTriggerCharacter) @@ -215,11 +212,11 @@ protected async Task VerifyCommitCharactersAsync(string initialMarkup, string te var document = workspace.CurrentSolution.GetDocument(documentId); var position = hostDocument.CursorPosition.Value; - var service = await GetCompletionServiceAsync(); + var service = GetCompletionService(workspace); var completionList = await GetCompletionListAsync(service, document, position, CompletionTrigger.Default); var item = completionList.Items.First(i => i.DisplayText.StartsWith(textTypedSoFar)); - var completionRules = CompletionHelper.GetHelper(document); + var completionRules = CompletionHelper.GetHelper(document, service); foreach (var ch in validChars) { diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/CrefCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/CrefCompletionProviderTests.cs index b7e7d5d36ebad..d8c08578cf234 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/CrefCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/CrefCompletionProviderTests.cs @@ -434,7 +434,7 @@ class C var provider = new CrefCompletionProvider(); var hostDocument = workspace.DocumentWithCursor; var document = workspace.CurrentSolution.GetDocument(hostDocument.Id); - var service = await GetCompletionServiceAsync(); + var service = GetCompletionService(workspace); var completionList = await GetCompletionListAsync(service, document, hostDocument.CursorPosition.Value, CompletionTrigger.Default); } } diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.cs index 692595df813a0..253078d572e48 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.cs @@ -1,12 +1,8 @@ // 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 System; -using System.Collections.Immutable; using System.Linq; -using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion; -using Microsoft.CodeAnalysis.CSharp.Completion; using Microsoft.CodeAnalysis.CSharp.Completion.Providers; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Roslyn.Test.Utilities; @@ -20,11 +16,6 @@ public ObjectInitializerCompletionProviderTests(CSharpTestWorkspaceFixture works { } - internal override CompletionServiceWithProviders CreateCompletionService(TestWorkspace workspace, ImmutableArray exclusiveProviders) - { - return new CSharpCompletionService(workspace, exclusiveProviders); - } - internal override CompletionProvider CreateCompletionProvider() { return new ObjectInitializerCompletionProvider(); @@ -584,11 +575,11 @@ void foo() var document = workspace.CurrentSolution.GetDocument(hostDocument.Id); var triggerInfo = CompletionTrigger.CreateInsertionTrigger('a'); - var service = await GetCompletionServiceAsync(); + var service = GetCompletionService(workspace); var completionList = await GetCompletionListAsync(service, document, position, triggerInfo); var item = completionList.Items.First(); - var completionRules = CompletionHelper.GetHelper(document); + var completionRules = CompletionHelper.GetHelper(document, service); Assert.False(completionRules.SendEnterThroughToEditor(item, string.Empty, workspace.Options), "Expected false from SendEnterThroughToEditor()"); } @@ -786,7 +777,7 @@ private async Task VerifyExclusiveAsync(string markup, bool exclusive) var document = workspace.CurrentSolution.GetDocument(hostDocument.Id); var triggerInfo = CompletionTrigger.CreateInsertionTrigger('a'); - var service = await GetCompletionServiceAsync(); + var service = GetCompletionService(workspace); var completionList = await GetCompletionListAsync(service, document, position, triggerInfo); if (completionList != null) diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/OverrideCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/OverrideCompletionProviderTests.cs index d3f0c958ae262..3af70e51beaa9 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/OverrideCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/OverrideCompletionProviderTests.cs @@ -2126,7 +2126,7 @@ public override void set_Bar(int bay, int value) var document = solution.GetDocument(documentId); var triggerInfo = CompletionTrigger.Default; - var service = await GetCompletionServiceAsync(); + var service = GetCompletionService(testWorkspace); var completionList = await GetCompletionListAsync(service, document, position, triggerInfo); var completionItem = completionList.Items.First(i => CompareItems(i.DisplayText, "Bar[int bay]")); @@ -2386,7 +2386,7 @@ public override bool Equals(object obj) var document = solution.GetDocument(documentId); var triggerInfo = CompletionTrigger.Default; - var service = await GetCompletionServiceAsync(); + var service = GetCompletionService(testWorkspace); var completionList = await GetCompletionListAsync(service, document, position, triggerInfo); var completionItem = completionList.Items.First(i => CompareItems(i.DisplayText, "Equals(object obj)")); @@ -2445,7 +2445,7 @@ public override bool Equals(object obj) var document = solution.GetDocument(documentId); var triggerInfo = CompletionTrigger.Default; - var service = await GetCompletionServiceAsync(); + var service = GetCompletionService(testWorkspace); var completionList = await GetCompletionListAsync(service, document, cursorPosition, triggerInfo); var completionItem = completionList.Items.First(i => CompareItems(i.DisplayText, "Equals(object obj)")); @@ -2544,22 +2544,24 @@ static void Main(string[] args) override $$ } }"; - var workspace = await TestWorkspace.CreateAsync(LanguageNames.CSharp, new CSharpCompilationOptions(OutputKind.ConsoleApplication), new CSharpParseOptions(), text); - var provider = new OverrideCompletionProvider(); - var testDocument = workspace.Documents.Single(); - var document = workspace.CurrentSolution.GetDocument(testDocument.Id); + using (var workspace = await TestWorkspace.CreateAsync(LanguageNames.CSharp, new CSharpCompilationOptions(OutputKind.ConsoleApplication), new CSharpParseOptions(), text)) + { + var provider = new OverrideCompletionProvider(); + var testDocument = workspace.Documents.Single(); + var document = workspace.CurrentSolution.GetDocument(testDocument.Id); - var service = await GetCompletionServiceAsync(); - var completionList = await GetCompletionListAsync(service, document, testDocument.CursorPosition.Value, CompletionTrigger.Default); + var service = GetCompletionService(workspace); + var completionList = await GetCompletionListAsync(service, document, testDocument.CursorPosition.Value, CompletionTrigger.Default); - var oldTree = await document.GetSyntaxTreeAsync(); + var oldTree = await document.GetSyntaxTreeAsync(); - var commit = await provider.GetChangeAsync(document, completionList.Items.First(i => i.DisplayText == "ToString()"), ' '); - var changes = commit.TextChanges; + var commit = await provider.GetChangeAsync(document, completionList.Items.First(i => i.DisplayText == "ToString()"), ' '); + var changes = commit.TextChanges; - // If we left the trailing trivia of the close curly of Main alone, - // there should only be one change: the replacement of "override " with a method. - Assert.Equal(changes.Single().Span, TextSpan.FromBounds(136, 145)); + // If we left the trailing trivia of the close curly of Main alone, + // there should only be one change: the replacement of "override " with a method. + Assert.Equal(changes.Single().Span, TextSpan.FromBounds(136, 145)); + } } } } \ No newline at end of file diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.cs index 6369990d64ab4..9883a440fc191 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.cs @@ -580,8 +580,10 @@ private async Task VerifyWorkerAsync(string markup, bool isBuilder) private async Task CheckResultsAsync(Document document, int position, bool isBuilder) { var triggerInfo = CompletionTrigger.CreateInsertionTrigger('a'); - var service = await GetCompletionServiceAsync(); - var completionList = await GetCompletionListAsync(service, document, position, triggerInfo); + var service = GetCompletionService(document.Project.Solution.Workspace); + var completionList = await service.GetContextAsync( + service.ExclusiveProviders?[0], document, position, triggerInfo, + options: null, cancellationToken: CancellationToken.None); if (isBuilder) { diff --git a/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelper.cs b/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelper.cs index 40db2eba6da43..8a8d48d7ff597 100644 --- a/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelper.cs +++ b/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelper.cs @@ -29,7 +29,8 @@ protected CompletionHelper(CompletionService completionService) _rules = CompletionService.GetRules(); } - public static CompletionHelper GetHelper(Workspace workspace, string language) + public static CompletionHelper GetHelper( + Workspace workspace, string language, CompletionService completionService) { var ls = workspace.Services.GetLanguageServices(language); if (ls != null) @@ -37,10 +38,9 @@ public static CompletionHelper GetHelper(Workspace workspace, string language) var factory = ls.GetService(); if (factory != null) { - return factory.CreateCompletionHelper(); + return factory.CreateCompletionHelper(completionService); } - var completionService = ls.GetService(); if (completionService != null) { return new CompletionHelper(completionService); @@ -50,9 +50,9 @@ public static CompletionHelper GetHelper(Workspace workspace, string language) return null; } - public static CompletionHelper GetHelper(Document document) + public static CompletionHelper GetHelper(Document document, CompletionService service) { - return GetHelper(document.Project.Solution.Workspace, document.Project.Language); + return GetHelper(document.Project.Solution.Workspace, document.Project.Language, service); } public IReadOnlyList GetHighlightedSpans(CompletionItem completionItem, string filterText) @@ -446,9 +446,10 @@ protected bool IsObjectCreationItem(CompletionItem item) return item.Tags.Contains(CompletionTags.ObjectCreation); } - public static async Task GetTextChangeAsync(Document document, CompletionItem item, char? commitKey = null, CancellationToken cancellationToken = default(CancellationToken)) + public static async Task GetTextChangeAsync( + CompletionService service, Document document, CompletionItem item, + char? commitKey = null, CancellationToken cancellationToken = default(CancellationToken)) { - var service = CompletionService.GetService(document); var change = await service.GetChangeAsync(document, item, commitKey, cancellationToken).ConfigureAwait(false); // normally the items that produce multiple changes are not expecting to trigger the behaviors that rely on looking at the text diff --git a/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelperFactory.cs b/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelperFactory.cs index 51b4dd76db51a..b4e70192d448c 100644 --- a/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelperFactory.cs +++ b/src/EditorFeatures/Core/Extensibility/Completion/CompletionHelperFactory.cs @@ -1,12 +1,13 @@ // 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 System; +using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.Editor { internal abstract class CompletionHelperFactory : ILanguageService { - public abstract CompletionHelper CreateCompletionHelper(); + public abstract CompletionHelper CreateCompletionHelper(CompletionService completionService); } } \ No newline at end of file diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_ReturnKey.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_ReturnKey.cs index c7958b00b1c4e..687f399a46987 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_ReturnKey.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_ReturnKey.cs @@ -1,9 +1,7 @@ // 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 System; -using System.Threading; using Microsoft.CodeAnalysis.Editor.Commands; -using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion { diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_TypeChar.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_TypeChar.cs index bb5da8ee21d19..d393f3daa829e 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_TypeChar.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_TypeChar.cs @@ -13,6 +13,7 @@ using Microsoft.VisualStudio.Text; using Roslyn.Utilities; using System.Threading; +using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion { @@ -273,7 +274,8 @@ private CompletionHelper GetCompletionHelper() var document = this.SubjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); if (document != null) { - return CompletionHelper.GetHelper(document); + return CompletionHelper.GetHelper( + document, document.GetLanguageService()); } return null; diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/DescriptionModifyingPresentationItem.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/DescriptionModifyingPresentationItem.cs index 04cc58b940ab4..5562af6653c7c 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/DescriptionModifyingPresentationItem.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/DescriptionModifyingPresentationItem.cs @@ -36,7 +36,7 @@ public override async Task GetDescriptionAsync(Document d var description = await this.CompletionService.GetDescriptionAsync(document, this.Item, cancellationToken).ConfigureAwait(false); var parts = description.TaggedParts; - var change = await CompletionHelper.GetTextChangeAsync(document, this.Item, '\t').ConfigureAwait(false); + var change = await CompletionHelper.GetTextChangeAsync(this.CompletionService, document, this.Item, '\t').ConfigureAwait(false); var insertionText = change.NewText; var note = string.Empty; diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/RoslynCompletionSet.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/RoslynCompletionSet.cs index a30f1b0f3bdbd..acdd8dc727043 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/RoslynCompletionSet.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/RoslynCompletionSet.cs @@ -12,7 +12,6 @@ using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using VSCompletion = Microsoft.VisualStudio.Language.Intellisense.Completion; -using Microsoft.CodeAnalysis.Snippets; namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion.Presentation { @@ -205,7 +204,8 @@ private CompletionHelper GetCompletionHelper() var document = _subjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); if (document != null) { - _completionHelper = CompletionHelper.GetHelper(document); + _completionHelper = CompletionHelper.GetHelper(document, + document.Project.LanguageServices.GetService()); } } diff --git a/src/EditorFeatures/Test/Completion/AbstractCompletionProviderTests.cs b/src/EditorFeatures/Test/Completion/AbstractCompletionProviderTests.cs index 4d44b10a50541..76abd17ffaefa 100644 --- a/src/EditorFeatures/Test/Completion/AbstractCompletionProviderTests.cs +++ b/src/EditorFeatures/Test/Completion/AbstractCompletionProviderTests.cs @@ -51,18 +51,17 @@ protected static async Task CanUseSpeculativeSemanticModelAsync(Document d return !service.GetMemberBodySpanForSpeculativeBinding(node).IsEmpty; } - internal async Task GetCompletionServiceAsync() + internal CompletionServiceWithProviders GetCompletionService(Workspace workspace) { - var workspace = await this.WorkspaceFixture.GetWorkspaceAsync(); return CreateCompletionService(workspace, ImmutableArray.Create(CreateCompletionProvider())); } internal abstract CompletionServiceWithProviders CreateCompletionService( - TestWorkspace workspace, ImmutableArray exclusiveProviders); + Workspace workspace, ImmutableArray exclusiveProviders); - internal static CompletionHelper GetCompletionHelper(Document document) + internal static CompletionHelper GetCompletionHelper(Document document, CompletionService service) { - return CompletionHelper.GetHelper(document); + return CompletionHelper.GetHelper(document, service); } internal static async Task GetCompletionListContextAsync( @@ -100,7 +99,7 @@ private async Task CheckResultsAsync( trigger = CompletionTrigger.CreateInsertionTrigger(insertedCharacter: code.ElementAt(position - 1)); } - var completionService = await GetCompletionServiceAsync(); + var completionService = GetCompletionService(document.Project.Solution.Workspace); var completionList = await GetCompletionListAsync(completionService, document, position, trigger); var items = completionList == null ? default(ImmutableArray) : completionList.Items; @@ -302,16 +301,17 @@ protected virtual async Task VerifyCustomCommitProviderWorkerAsync(string codeBe private async Task VerifyCustomCommitProviderCheckResultsAsync(Document document, string codeBeforeCommit, int position, string itemToCommit, string expectedCodeAfterCommit, char? commitChar) { - var textBuffer = (await WorkspaceFixture.GetWorkspaceAsync()).Documents.Single().TextBuffer; + var workspace = await WorkspaceFixture.GetWorkspaceAsync(); + var textBuffer = workspace.Documents.Single().TextBuffer; - var service = await GetCompletionServiceAsync(); + var service = GetCompletionService(workspace); var items = (await GetCompletionListAsync(service, document, position, CompletionTrigger.Default)).Items; var firstItem = items.First(i => CompareItems(i.DisplayText, itemToCommit)); var customCommitCompletionProvider = service.ExclusiveProviders?[0] as ICustomCommitCompletionProvider; if (customCommitCompletionProvider != null) { - var completionRules = GetCompletionHelper(document); + var completionRules = GetCompletionHelper(document, service); var textView = (await WorkspaceFixture.GetWorkspaceAsync()).Documents.Single().GetTextView(); VerifyCustomCommitWorker(customCommitCompletionProvider, firstItem, completionRules, textView, textBuffer, codeBeforeCommit, expectedCodeAfterCommit, commitChar); } @@ -333,7 +333,7 @@ internal async Task VerifyCustomCommitWorkerAsync( string actualExpectedCode = null; MarkupTestFile.GetPosition(expectedCodeAfterCommit, out actualExpectedCode, out expectedCaretPosition); - CompletionHelper completionRules = GetCompletionHelper(document); + CompletionHelper completionRules = GetCompletionHelper(document, service); if (commitChar.HasValue && !completionRules.IsCommitCharacter(completionItem, commitChar.Value, string.Empty)) { @@ -409,21 +409,22 @@ protected virtual async Task VerifyProviderCommitWorkerAsync(string codeBeforeCo private async Task VerifyProviderCommitCheckResultsAsync(Document document, int position, string itemToCommit, string expectedCodeAfterCommit, char? commitCharOpt, string textTypedSoFar) { - var textBuffer = (await WorkspaceFixture.GetWorkspaceAsync()).Documents.Single().TextBuffer; + var workspace = await WorkspaceFixture.GetWorkspaceAsync(); + var textBuffer = workspace.Documents.Single().TextBuffer; var textSnapshot = textBuffer.CurrentSnapshot.AsText(); - var service = await GetCompletionServiceAsync(); + var service = GetCompletionService(workspace); var items = (await GetCompletionListAsync(service, document, position, CompletionTrigger.Default)).Items; var firstItem = items.First(i => CompareItems(i.DisplayText, itemToCommit)); - var completionRules = GetCompletionHelper(document); + var completionRules = GetCompletionHelper(document, service); var commitChar = commitCharOpt ?? '\t'; var text = await document.GetTextAsync(); if (commitChar == '\t' || completionRules.IsCommitCharacter(firstItem, commitChar, textTypedSoFar)) { - var textChange = CompletionHelper.GetTextChangeAsync(document, firstItem, commitChar).Result; + var textChange = CompletionHelper.GetTextChangeAsync(service, document, firstItem, commitChar).Result; // Adjust TextChange to include commit character, so long as it isn't TAB. if (commitChar != '\t') @@ -550,7 +551,7 @@ private async Task VerifyItemWithReferenceWorkerAsync( var triggerInfo = CompletionTrigger.Default; - var completionService = await GetCompletionServiceAsync(); + var completionService = GetCompletionService(testWorkspace); var completionList = await GetCompletionListAsync(completionService, document, position, triggerInfo); if (expectedSymbols >= 1) @@ -605,7 +606,7 @@ private async Task VerifyItemWithMscorlib45WorkerAsync( var document = solution.GetDocument(documentId); var triggerInfo = CompletionTrigger.Default; - var completionService = await GetCompletionServiceAsync(); + var completionService = GetCompletionService(testWorkspace); var completionList = await GetCompletionListAsync(completionService, document, position, triggerInfo); var item = completionList.Items.FirstOrDefault(i => i.DisplayText == expectedItem); @@ -637,7 +638,7 @@ protected async Task VerifyItemInLinkedFilesAsync(string xmlString, string expec var document = solution.GetDocument(currentContextDocumentId); var triggerInfo = CompletionTrigger.Default; - var completionService = await GetCompletionServiceAsync(); + var completionService = GetCompletionService(testWorkspace); var completionList = await GetCompletionListAsync(completionService, document, position, triggerInfo); var item = completionList.Items.Single(c => c.DisplayText == expectedItem); diff --git a/src/EditorFeatures/Test2/IntelliSense/CompletionRulesTests.vb b/src/EditorFeatures/Test2/IntelliSense/CompletionRulesTests.vb index 56536d16a0a25..d0c49dfc4d1e6 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CompletionRulesTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CompletionRulesTests.vb @@ -47,7 +47,9 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Private Sub TestMatches(v As String, wordsToMatch() As String) Using New CultureContext("tr-TR") - Dim helper = CompletionHelper.GetHelper(New TestWorkspace, LanguageNames.CSharp) + Dim workspace = New TestWorkspace + Dim helper = CompletionHelper.GetHelper(workspace, LanguageNames.CSharp, + workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetService(Of CompletionService)) For Each word In wordsToMatch Dim item = CompletionItem.Create(word) Assert.True(helper.MatchesFilterText(item, v, CompletionTrigger.Default, CompletionFilterReason.TypeChar), $"Expected item {word} does not match {v}") @@ -57,7 +59,9 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Private Sub TestNotMatches(v As String, wordsToNotMatch() As String) Using New CultureContext("tr-TR") - Dim helper = CompletionHelper.GetHelper(New TestWorkspace, LanguageNames.CSharp) + Dim workspace = New TestWorkspace + Dim helper = CompletionHelper.GetHelper(workspace, LanguageNames.CSharp, + workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetService(Of CompletionService)) For Each word In wordsToNotMatch Dim item = CompletionItem.Create(word) Assert.False(helper.MatchesFilterText(item, v, CompletionTrigger.Default, CompletionFilterReason.TypeChar), $"Unexpected item {word} matches {v}") diff --git a/src/EditorFeatures/VisualBasic/Completion/VisualBasicCompletionHelper.vb b/src/EditorFeatures/VisualBasic/Completion/VisualBasicCompletionHelper.vb index ab8d2986551d8..5924ec733076d 100644 --- a/src/EditorFeatures/VisualBasic/Completion/VisualBasicCompletionHelper.vb +++ b/src/EditorFeatures/VisualBasic/Completion/VisualBasicCompletionHelper.vb @@ -13,21 +13,17 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.Completion Implements ILanguageServiceFactory Public Function CreateLanguageService(languageServices As HostLanguageServices) As ILanguageService Implements ILanguageServiceFactory.CreateLanguageService - Dim completionService = languageServices.GetService(Of CompletionService) - Return New VisualBasicCompletionHelperFactory(completionService) + Return New VisualBasicCompletionHelperFactory() End Function Private Class VisualBasicCompletionHelperFactory Inherits CompletionHelperFactory - Private _completionService As CompletionService - - Public Sub New(completionService As CompletionService) - _completionService = completionService + Public Sub New() End Sub - Public Overrides Function CreateCompletionHelper() As CompletionHelper - Return New VisualBasicCompletionHelper(_completionService) + Public Overrides Function CreateCompletionHelper(service As CompletionService) As CompletionHelper + Return New VisualBasicCompletionHelper(service) End Function End Class End Class diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AbstractVisualBasicCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AbstractVisualBasicCompletionProviderTests.vb index a1289ce9c3186..64a4c2013f5f5 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AbstractVisualBasicCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AbstractVisualBasicCompletionProviderTests.vb @@ -16,7 +16,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet MyBase.New(workspaceFixture) End Sub - Friend Overrides Function CreateCompletionService(workspace As TestWorkspace, exclusiveProviders As ImmutableArray(Of CompletionProvider)) As CompletionServiceWithProviders + Friend Overrides Function CreateCompletionService(workspace As Workspace, exclusiveProviders As ImmutableArray(Of CompletionProvider)) As CompletionServiceWithProviders Return New VisualBasicCompletionService(workspace, exclusiveProviders) End Function @@ -79,7 +79,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet Await MyBase.VerifyWorkerAsync(code, position, expectedItemOrNull, expectedDescriptionOrNull, sourceCodeKind, usePreviousCharAsTrigger, checkForAbsence, experimental:=experimental, glyph:=glyph) End Function - Private Function VerifyAtEndOfFileAsync(code As String, position As Integer, expectedItemOrNull As String, expectedDescriptionOrNull As String, sourceCodeKind As SourceCodeKind, usePreviousCharAsTrigger As Boolean, checkForAbsence As Boolean, glyph As Integer?, experimental As Boolean) As Threading.Tasks.Task + Protected Function VerifyAtEndOfFileAsync(code As String, position As Integer, expectedItemOrNull As String, expectedDescriptionOrNull As String, sourceCodeKind As SourceCodeKind, usePreviousCharAsTrigger As Boolean, checkForAbsence As Boolean, glyph As Integer?, experimental As Boolean) As Threading.Tasks.Task Return VerifyAtEndOfFileAsync(code, position, String.Empty, expectedItemOrNull, expectedDescriptionOrNull, sourceCodeKind, usePreviousCharAsTrigger, checkForAbsence, glyph, experimental) End Function @@ -117,11 +117,11 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet Dim document = workspace.CurrentSolution.GetDocument(documentId) Dim position = hostDocument.CursorPosition.Value - Dim service = Await GetCompletionServiceAsync() + Dim service = GetCompletionService(workspace) Dim completionList = Await GetCompletionListAsync(service, document, position, CompletionTrigger.Default) Dim item = completionList.Items.First(Function(i) i.DisplayText.StartsWith(textTypedSoFar)) - Dim helper = CompletionHelper.GetHelper(document) + Dim helper = CompletionHelper.GetHelper(document, service) Assert.Equal(expected, helper.SendEnterThroughToEditor(item, textTypedSoFar, workspace.Options)) End Using End Function @@ -138,11 +138,11 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet Dim document = workspace.CurrentSolution.GetDocument(documentId) Dim position = hostDocument.CursorPosition.Value - Dim service = Await GetCompletionServiceAsync() + Dim service = GetCompletionService(workspace) Dim completionList = Await GetCompletionListAsync(service, document, position, CompletionTrigger.Default) Dim item = completionList.Items.First() - Dim helper = CompletionHelper.GetHelper(document) + Dim helper = CompletionHelper.GetHelper(document, service) For Each ch In chars Assert.True(helper.IsCommitCharacter(item, ch, textTypedSoFar), $"Expected '{ch}' to be a commit character") @@ -206,7 +206,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet Dim options = workspace.Options.WithChangedOption(CompletionOptions.TriggerOnTypingLetters, LanguageNames.VisualBasic, triggerOnLetter) Dim trigger = CompletionTrigger.CreateInsertionTrigger(text(position)) - Dim completionService = Await GetCompletionServiceAsync() + Dim completionService = GetCompletionService(workspace) Dim isTextualTriggerCharacterResult = completionService.ShouldTriggerCompletion( text, position + 1, trigger, options:=options) diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/CrefCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/CrefCompletionProviderTests.vb index 3533f5f8e3d9b..4d27b806a1c4f 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/CrefCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/CrefCompletionProviderTests.vb @@ -431,7 +431,7 @@ End Class]]>.Value.NormalizeLineEndings() Dim hostDocument = workspace.DocumentWithCursor Dim document = workspace.CurrentSolution.GetDocument(hostDocument.Id) - Dim service = Await GetCompletionServiceAsync() + Dim service = GetCompletionService(workspace) Dim completionList = Await GetCompletionListAsync(service, document, hostDocument.CursorPosition.Value, CompletionTrigger.Default) End Using End Function diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ImplementsClauseCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ImplementsClauseCompletionProviderTests.vb index 532886f058ca4..15f211222ab30 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ImplementsClauseCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ImplementsClauseCompletionProviderTests.vb @@ -643,7 +643,7 @@ End Interface Using workspace = Await TestWorkspace.CreateAsync(element) Dim position = workspace.Documents.Single().CursorPosition.Value Dim document = workspace.CurrentSolution.GetDocument(workspace.Documents.Single().Id) - Dim service = Await GetCompletionServiceAsync() + Dim service = GetCompletionService(workspace) Dim completionList = Await GetCompletionListAsync(service, document, position, CompletionTrigger.Default) AssertEx.Any(completionList.Items, Function(c) c.DisplayText = "Workcover") End Using diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.vb index c2300cd48a83e..b25bcf393e7aa 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.vb @@ -406,7 +406,7 @@ End Program Dim hostDocument = workspace.Documents.First() Dim caretPosition = hostDocument.CursorPosition.Value Dim document = workspace.CurrentSolution.GetDocument(hostDocument.Id) - Dim service = Await GetCompletionServiceAsync() + Dim service = GetCompletionService(workspace) Dim completionList = Await GetCompletionListAsync(service, document, caretPosition, CompletionTrigger.Default) Assert.True(completionList Is Nothing OrElse completionList.IsExclusive, "Expected always exclusive") End Using diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/OverrideCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/OverrideCompletionProviderTests.vb index 9ac0465867d9f..3281d3122a9ef 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/OverrideCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/OverrideCompletionProviderTests.vb @@ -1746,7 +1746,7 @@ public class C Dim caretPosition = hostDocument.CursorPosition.Value Dim document = workspace.CurrentSolution.GetDocument(hostDocument.Id) - Dim service = Await GetCompletionServiceAsync() + Dim service = GetCompletionService(workspace) Dim completionList = Await GetCompletionListAsync(service, document, caretPosition, CompletionTrigger.Default) Assert.False(completionList.Items.Any(Function(c) c.DisplayText = "e")) End Using diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.vb index 78b8c3b5f5dca..553ad04a246ef 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.vb @@ -1,5 +1,6 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +Imports System.Threading Imports System.Threading.Tasks Imports System.Xml.Linq Imports Microsoft.CodeAnalysis.Completion @@ -328,21 +329,21 @@ End Class Await CheckResultsAsync(document2, position, isBuilder, triggerInfo, options) End If End Using - End Function Private Async Function CheckResultsAsync(document As Document, position As Integer, isBuilder As Boolean, triggerInfo As CompletionTrigger?, options As OptionSet) As Task triggerInfo = If(triggerInfo, CompletionTrigger.CreateInsertionTrigger("a"c)) - Dim service = Await GetCompletionServiceAsync() - Dim completionList = Await GetCompletionListAsync(service, document, position, triggerInfo.Value, options) + Dim service = GetCompletionService(document.Project.Solution.Workspace) + Dim context = Await service.GetContextAsync( + service.ExclusiveProviders?(0), document, position, triggerInfo.Value, options, CancellationToken.None) If isBuilder Then - Assert.NotNull(completionList) - Assert.NotNull(completionList.SuggestionModeItem) + Assert.NotNull(context) + Assert.NotNull(context.SuggestionModeItem) Else - If completionList IsNot Nothing Then - Assert.True(completionList.SuggestionModeItem Is Nothing, "group.Builder = " & If(completionList.SuggestionModeItem IsNot Nothing, completionList.SuggestionModeItem.DisplayText, "null")) + If context IsNot Nothing Then + Assert.True(context.SuggestionModeItem Is Nothing, "group.Builder = " & If(context.SuggestionModeItem IsNot Nothing, context.SuggestionModeItem.DisplayText, "null")) End If End If End Function diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/XmlDocCommentCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/XmlDocCommentCompletionProviderTests.vb index ae628ac4cbbb8..8a2e53afba789 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/XmlDocCommentCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/XmlDocCommentCompletionProviderTests.vb @@ -1,5 +1,6 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.Completion Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.CompletionProviders @@ -17,6 +18,11 @@ Namespace Tests Return New XmlDocCommentCompletionProvider() End Function + Protected Overrides Async Function VerifyWorkerAsync(code As String, position As Integer, expectedItemOrNull As String, expectedDescriptionOrNull As String, sourceCodeKind As SourceCodeKind, usePreviousCharAsTrigger As Boolean, checkForAbsence As Boolean, experimental As Boolean, glyph As Integer?) As Task + Await VerifyAtPositionAsync(code, position, expectedItemOrNull, expectedDescriptionOrNull, sourceCodeKind, usePreviousCharAsTrigger, checkForAbsence, glyph, experimental) + Await VerifyAtEndOfFileAsync(code, position, expectedItemOrNull, expectedDescriptionOrNull, sourceCodeKind, usePreviousCharAsTrigger, checkForAbsence, glyph, experimental) + End Function + Private Async Function VerifyItemsExistAsync(markup As String, ParamArray items() As String) As Task For Each item In items Await VerifyItemExistsAsync(markup, item) @@ -340,7 +346,9 @@ Class C End Class " - Await VerifyItemExistsAsync(text, "foo", usePreviousCharAsTrigger:=True) + Await VerifyItemExistsAsync( + text, "foo", + usePreviousCharAsTrigger:=True) End Function diff --git a/src/Features/CSharp/Portable/Completion/SuggestionMode/CSharpSuggestionModeCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/SuggestionMode/CSharpSuggestionModeCompletionProvider.cs index f85ffb392a787..6838be4de173e 100644 --- a/src/Features/CSharp/Portable/Completion/SuggestionMode/CSharpSuggestionModeCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/SuggestionMode/CSharpSuggestionModeCompletionProvider.cs @@ -1,5 +1,6 @@ // 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 System; using System.Linq; using System.Threading; using System.Threading.Tasks; diff --git a/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs b/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs index dfb1065b8a4b2..99d0d41fcf07f 100644 --- a/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs +++ b/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs @@ -205,7 +205,9 @@ public override async Task GetCompletionsAsync( var completionLists = new List(); foreach (var provider in triggeredProviders) { - var completionList = await GetProviderCompletionsAsync(provider, document, caretPosition, defaultItemSpan, trigger, options, cancellationToken).ConfigureAwait(false); + var completionList = await GetContextAsync( + provider, document, caretPosition, trigger, + options, defaultItemSpan, cancellationToken).ConfigureAwait(false); if (completionList != null) { completionLists.Add(completionList); @@ -240,7 +242,7 @@ public override async Task GetCompletionsAsync( var nonUsedNonExclusiveLists = new List(); foreach (var provider in nonUsedProviders) { - var completionList = await GetProviderCompletionsAsync(provider, document, caretPosition, defaultItemSpan, trigger, options, cancellationToken).ConfigureAwait(false); + var completionList = await GetContextAsync(provider, document, caretPosition, trigger, options, defaultItemSpan, cancellationToken).ConfigureAwait(false); if (completionList != null && !completionList.IsExclusive) { nonUsedNonExclusiveLists.Add(completionList); @@ -290,7 +292,8 @@ private CompletionList MergeAndPruneCompletionLists( totalItems.Sort(); return CompletionList.Create( - contextSpan, totalItems.ToImmutableArray(), this.GetRules(), suggestionModeItem); + contextSpan, totalItems.ToImmutableArray(), this.GetRules(), suggestionModeItem, + isExclusive); } private void AddToDisplayMap( @@ -350,16 +353,38 @@ private Dictionary GetCompletionProviderToIndex(IEnumer return result; } - private static async Task GetProviderCompletionsAsync( + // Internal for testing purposes only. + internal async Task GetContextAsync( CompletionProvider provider, Document document, int position, - TextSpan defaultFilterSpan, CompletionTrigger triggerInfo, OptionSet options, CancellationToken cancellationToken) { - var context = new CompletionContext(provider, document, position, defaultFilterSpan, triggerInfo, options, cancellationToken); + return await GetContextAsync( + provider, document, position, triggerInfo, + options, defaultSpan: null, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + private async Task GetContextAsync( + CompletionProvider provider, + Document document, + int position, + CompletionTrigger triggerInfo, + OptionSet options, + TextSpan? defaultSpan, + CancellationToken cancellationToken) + { + options = options ?? document.Project.Solution.Workspace.Options; + + if (defaultSpan == null) + { + var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + defaultSpan = this.GetDefaultItemSpan(text, position); + } + + var context = new CompletionContext(provider, document, position, defaultSpan.Value, triggerInfo, options, cancellationToken); await provider.ProvideCompletionsAsync(context).ConfigureAwait(false); return context; } diff --git a/src/Interactive/EditorFeatures/Core/Completion/AbstractReferenceDirectiveCompletionProvider.cs b/src/Interactive/EditorFeatures/Core/Completion/AbstractReferenceDirectiveCompletionProvider.cs index 8d333c310fb60..d8962aea2ec56 100644 --- a/src/Interactive/EditorFeatures/Core/Completion/AbstractReferenceDirectiveCompletionProvider.cs +++ b/src/Interactive/EditorFeatures/Core/Completion/AbstractReferenceDirectiveCompletionProvider.cs @@ -40,7 +40,8 @@ private static ICurrentWorkingDirectoryDiscoveryService GetFileSystemDiscoverySe private static readonly ImmutableArray s_filterRules = ImmutableArray.Empty; - private static readonly CompletionItemRules s_rules = CompletionItemRules.Create(filterCharacterRules: s_filterRules, commitCharacterRules: s_commitRules); + private static readonly CompletionItemRules s_rules = CompletionItemRules.Create( + filterCharacterRules: s_filterRules, commitCharacterRules: s_commitRules, enterKeyRule: EnterKeyRule.Never); public override async Task ProvideCompletionsAsync(CompletionContext context) { From b4f7d45b371d9d5b650954642fa265decd832f0a Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 18 May 2016 22:44:34 -0700 Subject: [PATCH 6/7] Disable running rule on test code. --- build/Rulesets/NonShippingProjectRules.ruleset | 1 + 1 file changed, 1 insertion(+) diff --git a/build/Rulesets/NonShippingProjectRules.ruleset b/build/Rulesets/NonShippingProjectRules.ruleset index 9c1648b78fa59..f176857b22284 100644 --- a/build/Rulesets/NonShippingProjectRules.ruleset +++ b/build/Rulesets/NonShippingProjectRules.ruleset @@ -15,5 +15,6 @@ + \ No newline at end of file From d3567c25d9a1c0a21aef0c572148da07bc421085 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 19 May 2016 12:45:30 -0700 Subject: [PATCH 7/7] Named parameter. --- .../Core/Portable/Completion/CompletionServiceWithProviders.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs b/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs index 99d0d41fcf07f..34416504a9d37 100644 --- a/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs +++ b/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs @@ -40,7 +40,7 @@ public abstract class CompletionServiceWithProviders : CompletionService, IEqual private IEnumerable> _importedProviders; protected CompletionServiceWithProviders(Workspace workspace) - : this(workspace, null) + : this(workspace, exclusiveProviders: null) { }