diff --git a/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.DefinitionTrackingContext.cs b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.DefinitionTrackingContext.cs index 041f4dd5bc581d8c896bb2ccf2b4c82e515b4c76..6c13e8c969222462c5843cff58fab234009c4464 100644 --- a/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.DefinitionTrackingContext.cs +++ b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.DefinitionTrackingContext.cs @@ -14,7 +14,7 @@ internal abstract partial class AbstractFindUsagesService /// Forwards notifications to an underlying /// while also keeping track of the definitions reported. /// - /// These can then be used by to report the + /// These can then be used by to report the /// definitions found to third parties in case they want to add any additional definitions /// to the results we present. /// diff --git a/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.cs b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.cs index 8e637b66271c70a4660124cc407964c734fcb3ca..de39707792ae8c1a6a1d2a45a441ec11d8711713 100644 --- a/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.cs +++ b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.cs @@ -1,13 +1,14 @@ // 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.Collections.Immutable; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.FindUsages; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.FindUsages { @@ -50,18 +51,27 @@ internal abstract partial class AbstractFindUsagesService : IFindUsagesService { var definitionTrackingContext = new DefinitionTrackingContext(context); - // NOTE: All ConFigureAwaits in this method need to pass 'true' so that - // we return to the caller's context. that's so the call to - // CallThirdPartyExtensionsAsync will happen on the UI thread. We need - // this to maintain the threading guarantee we had around that method - // from pre-Roslyn days. + // Need ConfigureAwait(true) here so we get back to the UI thread before calling + // GetThirdPartyDefinitions. We need to call that on the UI thread to match behavior + // of how the language service always worked in the past. + // + // Any async calls before GetThirdPartyDefinitions must be ConfigureAwait(true). await FindLiteralOrSymbolReferencesAsync( document, position, definitionTrackingContext).ConfigureAwait(true); // After the FAR engine is done call into any third party extensions to see // if they want to add results. - await CallThirdPartyExtensionsAsync( - document.Project.Solution, definitionTrackingContext, context).ConfigureAwait(true); + var thirdPartyDefinitions = GetThirdPartyDefinitions( + document.Project.Solution, definitionTrackingContext.GetDefinitions(), context.CancellationToken); + + // From this point on we can do ConfigureAwait(false) as we're not calling back + // into third parties anymore. + + foreach (var definition in thirdPartyDefinitions) + { + // Don't need ConfigureAwait(true) here + await context.OnDefinitionFoundAsync(definition).ConfigureAwait(false); + } } private async Task FindLiteralOrSymbolReferencesAsync( @@ -81,24 +91,15 @@ internal abstract partial class AbstractFindUsagesService : IFindUsagesService document, position, context).ConfigureAwait(false); } - private async Task CallThirdPartyExtensionsAsync( + private ImmutableArray GetThirdPartyDefinitions( Solution solution, - DefinitionTrackingContext definitionTrackingContext, - IFindUsagesContext underlyingContext) + ImmutableArray definitions, + CancellationToken cancellationToken) { - var cancellationToken = definitionTrackingContext.CancellationToken; var factory = solution.Workspace.Services.GetService(); - - foreach (var definition in definitionTrackingContext.GetDefinitions()) - { - var item = factory.GetThirdPartyDefinitionItem(solution, definition, cancellationToken); - if (item != null) - { - // ConfigureAwait(true) because we want to come back on the - // same thread after calling into extensions. - await underlyingContext.OnDefinitionFoundAsync(item).ConfigureAwait(true); - } - } + return definitions.Select(d => factory.GetThirdPartyDefinitionItem(solution, d, cancellationToken)) + .WhereNotNull() + .ToImmutableArray(); } private async Task FindSymbolReferencesAsync(