Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Always try to use cached data for import completion even if outdated #59760

Merged
merged 22 commits into from
Mar 22, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
427d9ea
Merge remote-tracking branch 'dotnet/main' into MultiPhaseCompletion
genlu Feb 9, 2022
ce6a216
Merge branch 'sessionData' into MultiPhaseCompletion2
genlu Feb 17, 2022
d84cca7
Merge remote-tracking branch 'dotnet/main' into MultiPhaseCompletion2
genlu Feb 24, 2022
e65f7fd
Use cached data for completion of unimported types
genlu Feb 25, 2022
67c6dad
Use cached data for completion of unimported extension methods
genlu Feb 25, 2022
c635be6
Try to refresh import completion cache in the background whenever com…
genlu Feb 25, 2022
bae71ac
Use AsyncBatchingWorkQueue for background cache updates
genlu Feb 26, 2022
e663da0
Fix tests
genlu Feb 28, 2022
9785979
clean up
genlu Mar 1, 2022
9053574
Fix tests
genlu Mar 3, 2022
f8f9782
Make sure background task to update cache always run
genlu Mar 3, 2022
cd6c665
Merge remote-tracking branch 'dotnet/main' into UseCache
genlu Mar 3, 2022
ea57f09
Expose ForceExpandedCompletionIndexCreation option to O#
genlu Mar 3, 2022
478bf38
Merge remote-tracking branch 'dotnet/main' into UseCache
genlu Mar 9, 2022
e760f26
Don't use Workspace.CurrentSolution when refreshing cache in background
genlu Mar 9, 2022
205766f
Use AsynchronousOperationListener for background workqueues
genlu Mar 10, 2022
f77a0ad
Update src/Tools/ExternalAccess/OmniSharp/Completion/OmniSharpComplet…
genlu Mar 10, 2022
534dca0
Merge remote-tracking branch 'dotnet/main' into UseCache
genlu Mar 15, 2022
5ecff82
Pass ThreadingContext.DisposalToken to workqueue for cache refresh
genlu Mar 15, 2022
c44946e
Remove namespace imports
genlu Mar 16, 2022
072defb
Refactoring
genlu Mar 18, 2022
ebe077f
pass the listener to the constructor
genlu Mar 21, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6629,9 +6629,9 @@ class C
End Using
End Function

<WpfTheory, CombinatorialData>
<WpfFact>
<Trait(Traits.Feature, Traits.Features.Completion)>
Public Async Function WarmUpTypeImportCompletionCache(populateCache As Boolean) As Task
Public Async Function WarmUpTypeImportCompletionCache() As Task

Using state = TestStateFactory.CreateTestStateFromWorkspace(
<Workspace>
Expand Down Expand Up @@ -6663,31 +6663,28 @@ namespace NS2

Dim document = state.Workspace.CurrentSolution.GetDocument(state.Workspace.Documents.Single(Function(d) d.Name = "C.cs").Id)

If populateCache Then
Dim service = state.Workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetRequiredService(Of ITypeImportCompletionService)()
Await service.WarmUpCacheAsync(document.Project, CancellationToken.None)
End If

Dim completionService = CType(document.GetLanguageService(Of CompletionService)(), CompletionServiceWithProviders)
completionService.GetTestAccessor().SuppressPartialSemantics()

state.Workspace.GlobalOptions.SetGlobalOption(New OptionKey(CompletionOptionsStorage.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp), True)

Dim service = state.Workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetRequiredService(Of ITypeImportCompletionService)()

service.QueueCacheWarmUpTask(document.Project)
Await state.WaitForAsynchronousOperationsAsync()

state.SendInvokeCompletionList()
Await state.WaitForUIRenderedAsync()

' w/o warming up cache in advance, the first time import completion get triggered we will not return anything from references
If populateCache Then
Await state.AssertCompletionItemsContain(displayText:="UnimportedType", displayTextSuffix:="")
Else
Await state.AssertCompletionItemsDoNotContainAny("UnimportedType")
End If
Await state.AssertCompletionItemsContain(displayText:="UnimportedType", displayTextSuffix:="")

service.QueueCacheWarmUpTask(document.Project)
Await state.WaitForAsynchronousOperationsAsync()
End Using
End Function

<WpfTheory, CombinatorialData>
<WpfFact>
<Trait(Traits.Feature, Traits.Features.Completion)>
Public Async Function WarmUpExtensionMethodImportCompletionCache(populateCache As Boolean) As Task
Public Async Function WarmUpExtensionMethodImportCompletionCache() As Task

Using state = TestStateFactory.CreateTestStateFromWorkspace(
<Workspace>
Expand Down Expand Up @@ -6724,22 +6721,17 @@ namespace NS2
Dim completionService = CType(document.GetLanguageService(Of CompletionService)(), CompletionServiceWithProviders)
completionService.GetTestAccessor().SuppressPartialSemantics()

If populateCache Then
Await ExtensionMethodImportCompletionHelper.WarmUpCacheAsync(document, CancellationToken.None)
End If
Await ExtensionMethodImportCompletionHelper.WarmUpCacheAsync(document.Project, CancellationToken.None)
Await state.WaitForAsynchronousOperationsAsync()

state.SendInvokeCompletionList()
Await state.WaitForUIRenderedAsync()

' w/o warming up cache in advance, the first time import completion get triggered we will not return anything from references
If populateCache Then
Await state.AssertCompletionItemsContain(displayText:="IntegerExtMethod", displayTextSuffix:="")
Else
Await state.AssertCompletionItemsDoNotContainAny("IntegerExtMethod")
End If
Await state.AssertCompletionItemsContain(displayText:="IntegerExtMethod", displayTextSuffix:="")

' Make sure any background work would be completed.
Await ExtensionMethodImportCompletionHelper.WarmUpCacheAsync(document, CancellationToken.None)
Await ExtensionMethodImportCompletionHelper.WarmUpCacheAsync(document.Project, CancellationToken.None)
Await state.WaitForAsynchronousOperationsAsync()
End Using
End Function

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,29 @@
using Microsoft.CodeAnalysis.Completion.Providers.ImportCompletion;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Shared.TestHooks;

namespace Microsoft.CodeAnalysis.CSharp.Completion.Providers
{
[ExportLanguageServiceFactory(typeof(ITypeImportCompletionService), LanguageNames.CSharp), Shared]
internal sealed class TypeImportCompletionServiceFactory : ILanguageServiceFactory
{
private readonly IAsynchronousOperationListenerProvider _provider;

[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public TypeImportCompletionServiceFactory()
public TypeImportCompletionServiceFactory(IAsynchronousOperationListenerProvider provider)
{
_provider = provider;
}

public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
=> new CSharpTypeImportCompletionService(languageServices.WorkspaceServices.Workspace);
=> new CSharpTypeImportCompletionService(languageServices.WorkspaceServices.Workspace, _provider.GetListener(FeatureAttribute.CompletionSet));

private class CSharpTypeImportCompletionService : AbstractTypeImportCompletionService
{
public CSharpTypeImportCompletionService(Workspace workspace)
: base(workspace)
public CSharpTypeImportCompletionService(Workspace workspace, IAsynchronousOperationListener listener)
: base(workspace, listener)
{
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ protected override void LogCommit()

protected override void WarmUpCacheInBackground(Document document)
{
_ = ExtensionMethodImportCompletionHelper.WarmUpCacheAsync(document, CancellationToken.None);
_ = ExtensionMethodImportCompletionHelper.WarmUpCacheAsync(document.Project, CancellationToken.None);
}

protected override async Task AddCompletionItemsAsync(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ protected override void LogCommit()
protected override void WarmUpCacheInBackground(Document document)
{
var typeImportCompletionService = document.GetRequiredLanguageService<ITypeImportCompletionService>();
_ = typeImportCompletionService.WarmUpCacheAsync(document.Project, CancellationToken.None);
typeImportCompletionService.QueueCacheWarmUpTask(document.Project);
}

protected override async Task AddCompletionItemsAsync(CompletionContext completionContext, SyntaxContext syntaxContext, HashSet<string> namespacesInScope, CancellationToken cancellationToken)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ namespace Microsoft.CodeAnalysis.Completion.Providers.ImportCompletion
{
internal abstract partial class AbstractTypeImportCompletionService : ITypeImportCompletionService
{
private readonly Workspace _workspace;
private readonly AsyncBatchingWorkQueue<Project> _workQueue;

private IImportCompletionCacheService<CacheEntry, CacheEntry> CacheService { get; }
Expand All @@ -33,25 +32,20 @@ internal abstract partial class AbstractTypeImportCompletionService : ITypeImpor

protected abstract string Language { get; }

internal AbstractTypeImportCompletionService(Workspace workspace)
internal AbstractTypeImportCompletionService(Workspace workspace, IAsynchronousOperationListener listener)
{
_workspace = workspace;
_workQueue = new(
TimeSpan.FromSeconds(1),
BatchUpdateCacheAsync,
AsynchronousOperationListenerProvider.NullListener,
listener,
CancellationToken.None);

CacheService = _workspace.Services.GetRequiredService<IImportCompletionCacheService<CacheEntry, CacheEntry>>();
CacheService = workspace.Services.GetRequiredService<IImportCompletionCacheService<CacheEntry, CacheEntry>>();
}

public Task WarmUpCacheAsync(Project? project, CancellationToken cancellationToken)
public void QueueCacheWarmUpTask(Project project)
{
if (project is null)
return Task.CompletedTask;

_workQueue.AddWork(project);
return _workQueue.WaitUntilCurrentBatchCompletesAsync();
}

public async Task<(ImmutableArray<ImmutableArray<CompletionItem>>, bool)> GetAllTopLevelTypesAsync(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Completion.Providers.ImportCompletion;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.TestHooks;
Expand All @@ -35,11 +36,27 @@ private class ExtensionMethodSymbolComputer
// The value indicates if we can reduce an extension method with this receiver type given receiver type.
private readonly ConcurrentDictionary<ITypeSymbol, bool> _checkedReceiverTypes;

private static readonly AsyncBatchingWorkQueue<Project> _workQueue = new(
TimeSpan.FromSeconds(1),
BatchUpdateCacheAsync,
AsynchronousOperationListenerProvider.NullListener,
CancellationToken.None);
private static readonly object _gate = new();
private static AsyncBatchingWorkQueue<Project>? _workQueue;

private static AsyncBatchingWorkQueue<Project> GetWorkQueue(Project project)
{
lock (_gate)
{
if (_workQueue is null)
{
var exportProvider = (IMefHostExportProvider)project.Solution.Workspace.Services.HostServices;
var listenerProvider = exportProvider.GetExports<AsynchronousOperationListenerProvider>().Single().Value;
Copy link
Member

@sharwell sharwell Mar 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Code shouldn't assume HostServices implements IMefHostExportProvider. Can we not pass the listener to the constructor of this type?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are commenting on an out-of-date snapshot, this is moved to https://github.com/dotnet/roslyn/pull/59760/files#diff-91071542ca3996ad4d7178bc276d40deaae3e2a3b7522531f29d869e1fb636ccR48

That said, I'm still using the same code to fet listener provider there. I assume I can still import the provider in the constructor safely?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

_workQueue = new(
TimeSpan.FromSeconds(1),
BatchUpdateCacheAsync,
listenerProvider.GetListener(FeatureAttribute.CompletionSet),
CancellationToken.None);
}

return _workQueue;
}
}

public ExtensionMethodSymbolComputer(
Document document,
Expand Down Expand Up @@ -78,15 +95,10 @@ public static async Task<ExtensionMethodSymbolComputer> CreateAsync(
/// <summary>
/// Force create/update all relevant indices
/// </summary>
public static Task UpdateCacheAsync(Project? project)
public static void QueueCacheWarmUpTask(Project project)
{
if (project is not null)
{
_workQueue.AddWork(project);
return _workQueue.WaitUntilCurrentBatchCompletesAsync();
}

return Task.CompletedTask;
var queue = GetWorkQueue(project);
queue.AddWork(project);
}

private static async ValueTask BatchUpdateCacheAsync(ImmutableArray<Project> projects, CancellationToken cancellationToken)
Expand Down Expand Up @@ -155,7 +167,7 @@ private static async ValueTask BatchUpdateCacheAsync(ImmutableArray<Project> pro
{
// If we are not force creating/updating the cache, an update task needs to be queued in background.
if (!forceCacheCreation)
_workQueue.AddWork(_originatingDocument.Project);
GetWorkQueue(_originatingDocument.Project).AddWork(_originatingDocument.Project);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,25 @@ namespace Microsoft.CodeAnalysis.Completion.Providers
/// <remarks>It runs out-of-proc if it's enabled</remarks>
internal static partial class ExtensionMethodImportCompletionHelper
{
public static async Task WarmUpCacheAsync(Document document, CancellationToken cancellationToken)
public static async Task WarmUpCacheAsync(Project project, CancellationToken cancellationToken)
{
var project = document.Project;
var client = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false);
if (client != null)
{
var result = await client.TryInvokeAsync<IRemoteExtensionMethodImportCompletionService>(
project,
(service, solutionInfo, cancellationToken) => service.WarmUpCacheAsync(
solutionInfo, document.Id, cancellationToken),
solutionInfo, project.Id, cancellationToken),
cancellationToken).ConfigureAwait(false);
}
else
{
await WarmUpCacheInCurrentProcessAsync(document).ConfigureAwait(false);
WarmUpCacheInCurrentProcess(project);
}
}

public static Task WarmUpCacheInCurrentProcessAsync(Document document)
=> ExtensionMethodSymbolComputer.UpdateCacheAsync(document.Project);
public static void WarmUpCacheInCurrentProcess(Project project)
=> ExtensionMethodSymbolComputer.QueueCacheWarmUpTask(project);

public static async Task<SerializableUnimportedExtensionMethods?> GetUnimportedExtensionMethodsAsync(
Document document,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ internal interface IRemoteExtensionMethodImportCompletionService
bool hideAdvancedMembers,
CancellationToken cancellationToken);

ValueTask WarmUpCacheAsync(PinnedSolutionInfo solutionInfo, DocumentId documentId, CancellationToken cancellationToken);
ValueTask WarmUpCacheAsync(PinnedSolutionInfo solutionInfo, ProjectId projectId, CancellationToken cancellationToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ internal interface ITypeImportCompletionService : ILanguageService
CompletionOptions options,
CancellationToken cancellationToken);

Task WarmUpCacheAsync(Project project, CancellationToken cancellationToken);
void QueueCacheWarmUpTask(Project project);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,30 @@ Imports System.Composition
Imports Microsoft.CodeAnalysis.Completion.Providers.ImportCompletion
Imports Microsoft.CodeAnalysis.Host
Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.Shared.TestHooks

Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers
<ExportLanguageServiceFactory(GetType(ITypeImportCompletionService), LanguageNames.VisualBasic), [Shared]>
Friend NotInheritable Class TypeImportCompletionServiceFactory
Implements ILanguageServiceFactory

Private ReadOnly _provider As IAsynchronousOperationListenerProvider

<ImportingConstructor>
<Obsolete(MefConstruction.ImportingConstructorMessage, True)>
Public Sub New()
Public Sub New(provider As IAsynchronousOperationListenerProvider)
_provider = provider
End Sub

Public Function CreateLanguageService(languageServices As HostLanguageServices) As ILanguageService Implements ILanguageServiceFactory.CreateLanguageService
Return New BasicTypeImportCompletionService(languageServices.WorkspaceServices.Workspace)
Return New BasicTypeImportCompletionService(languageServices.WorkspaceServices.Workspace, _provider.GetListener(FeatureAttribute.CompletionSet))
End Function

Private Class BasicTypeImportCompletionService
Inherits AbstractTypeImportCompletionService

Public Sub New(workspace As Workspace)
MyBase.New(workspace)
Public Sub New(workspace As Workspace, listener As IAsynchronousOperationListener)
MyBase.New(workspace, listener)
End Sub

Protected Overrides ReadOnly Property GenericTypeSuffix As String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ protected override async Task InitializeServiceForOpenedDocumentAsync(Document d
return;

var service = document.GetRequiredLanguageService<ITypeImportCompletionService>();
await service.WarmUpCacheAsync(document.Project, CancellationToken.None).ConfigureAwait(false);
await ExtensionMethodImportCompletionHelper.WarmUpCacheAsync(document, CancellationToken.None).ConfigureAwait(false);
service.QueueCacheWarmUpTask(document.Project);
await ExtensionMethodImportCompletionHelper.WarmUpCacheAsync(document.Project, CancellationToken.None).ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,13 @@ public RemoteExtensionMethodImportCompletionService(in ServiceConstructionArgume
}, cancellationToken);
}

public ValueTask WarmUpCacheAsync(PinnedSolutionInfo solutionInfo, DocumentId documentId, CancellationToken cancellationToken)
public ValueTask WarmUpCacheAsync(PinnedSolutionInfo solutionInfo, ProjectId projectId, CancellationToken cancellationToken)
{
return RunServiceAsync(async cancellationToken =>
{
var solution = await GetSolutionAsync(solutionInfo, cancellationToken).ConfigureAwait(false);
var document = solution.GetRequiredDocument(documentId);
await ExtensionMethodImportCompletionHelper.WarmUpCacheInCurrentProcessAsync(document).ConfigureAwait(false);
var project = solution.GetRequiredProject(projectId);
ExtensionMethodImportCompletionHelper.WarmUpCacheInCurrentProcess(project);
}, cancellationToken);
}
}
Expand Down