diff --git a/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs b/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs index 457c8b69490ddded02a762038afbd4857fcb3ebe..31216f75ff9c38c5b3b51be16dacca30d661c1b3 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 @@ protected virtual ImmutableArray GetBuiltInProviders() 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 @@ protected virtual ImmutableArray GetProviders(ImmutableHashS 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(