Use IAsyncEnumerable in DesignerAttributeScanning as well.#64616
Use IAsyncEnumerable in DesignerAttributeScanning as well.#64616CyrusNajmabadi merged 27 commits intodotnet:mainfrom
Conversation
| [Fact] | ||
| public async Task NoDesignerTest1() | ||
| private static readonly TestComposition s_inProcessComposition = EditorTestCompositions.EditorFeatures; | ||
| private static readonly TestComposition s_outOffProcessComposition = s_inProcessComposition.WithTestHostParts(TestHost.OutOfProcess); |
There was a problem hiding this comment.
yaay, now we actually test oop for this feature.
|
@jasonmalinowski this is ready for review. |
| var changedData = await ComputeChangedDataAsync( | ||
| project, specificDocument, projectVersion, designerCategoryType, cancellationToken).ConfigureAwait(false); | ||
|
|
||
| // Only bother reporting non-empty information to save an unnecessary RPC. |
There was a problem hiding this comment.
this isn't necessary anymore. by definition, if we don't yield something, we have nothing to send over the rpc channel.
|
Moving back to draft. I realize we do something slightly funky here where we're mutating state in the middle of streamign the data, and we have to know exactly what both sides think teh state of the world is. |
| /// Keep track of the last information we reported. We will avoid notifying the host if we recompute and these | ||
| /// don't change. | ||
| /// </summary> | ||
| private readonly ConcurrentDictionary<DocumentId, (string? category, VersionStamp projectVersion)> _documentToLastReportedInformation = new(); |
There was a problem hiding this comment.
this is the remote side. it became completely stateless (which is good). there was a lot of complexity about the remote side trying to keep track of what hte host side might now and preventing updates. this was error prone and doesn't work in the IASyncEnumerable world. e.g. in remote IAE, jsut because you yielded the data, you dont' know that hte host has actually gotten it.
this moved to a cleaner model where the remote side knows nothing, and the host side keeps track.
| { | ||
| var data = await ComputeDesignerAttributeDataAsync(designerCategoryType, priorityDocument, cancellationToken).ConfigureAwait(false); | ||
| if (data != null) | ||
| yield return data.Value; |
There was a problem hiding this comment.
this is much cleaner too. we only send the data for things that actually have a category. in practice this is a tiny handful of messages in a few projects. This is different from before when we'd send info about every document, even those without a category.
| ImmutableArray<string> kinds, | ||
| CancellationToken cancellationToken) | ||
| { | ||
| return StreamWithSolutionAsync(solutionChecksum, SearchDocumentWorkerAsync, cancellationToken).WithJsonRpcSettings( |
There was a problem hiding this comment.
these changes were a request from Jason from teh last pr.
| @@ -5,24 +5,24 @@ | |||
| using System; | |||
There was a problem hiding this comment.
the meat of the change is in this type. This is the host-side of the feature. so this is the side that 'pulls' on the remote side to do the computation, then 'pushes' that data into the project system.
@DustinCampbell @tmeschter, ideally in teh future we can just have teh project system pull on this data using LSP, instead of this hybrid push/pull model.
src/VisualStudio/Core/Def/DesignerAttribute/VisualStudioDesignerAttributeService.cs
Outdated
Show resolved
Hide resolved
src/VisualStudio/Core/Def/DesignerAttribute/VisualStudioDesignerAttributeService.cs
Outdated
Show resolved
Hide resolved
| foreach (var (documentId, lastReportedData) in _documentToLastReportedData) | ||
| { | ||
| if (documentId.ProjectId == projectId && | ||
| !seenDocuments.Contains(documentId)) |
There was a problem hiding this comment.
should we check that the document still exists before reporting a null category, or does the project system handle that
There was a problem hiding this comment.
we check for the legacy project system (where we're on the UI thread, and where we need an ItemId). But we don't check on hte CPS case because it's inherently 'racey'. we're issueing on the BG, so there's no way for us to ever know for sure. the API is resilient to this though.
src/VisualStudio/Core/Def/DesignerAttribute/VisualStudioDesignerAttributeService.cs
Outdated
Show resolved
Hide resolved
…erAttributeService.cs Co-authored-by: David Barbet <dibarbet@gmail.com>
…mabadi/roslyn into designerAttributeStream
| /// we can dump our cached data for them. | ||
| /// </para> | ||
| /// </summary> | ||
| private readonly AsyncBatchingWorkQueue<(CodeAnalysis.Solution? solution, DesignerAttributeData? data)> _projectSystemNotificationQueue; |
There was a problem hiding this comment.
the PS queue now gets both the data-values, and the last solution we computed against. with that solution, it can dump old data for documents that are gone.
| if (!_documentToLastReportedData.TryGetValue(data.DocumentId, out var existingData) || | ||
| existingData.Category != data.Category) | ||
| { | ||
| changedData.Add(data); |
There was a problem hiding this comment.
so it looks like we compare the changed docIds (aka everything that came from the dataList that wasn't null) to the last reported docIds and only trigger notifications for ones that are different which seems fine.
But where do we compare the last reported docs that are no longer present in the new data (aka the designer attribute was removed)?
There was a problem hiding this comment.
sigh. good catch. i'm too tired.
dibarbet
left a comment
There was a problem hiding this comment.
looks good - one possible improvement suggestion
src/VisualStudio/Core/Def/DesignerAttribute/VisualStudioDesignerAttributeService.cs
Show resolved
Hide resolved
| if (projectId == priorityDocument?.Id.ProjectId) | ||
| continue; | ||
|
|
||
| await ProcessProjectAsync(client, solution.GetRequiredProject(projectId), priorityDocumentId: null, cancellationToken).ConfigureAwait(false); |
There was a problem hiding this comment.
This this throwing exceptions in RPS
Throw(StreamJsonRpc.RemoteInvocationException) Project of ID (ProjectId, #62aad7d9-16aa-4839-8e9c-7cd963aa2784 - /dev/null/inferredProject1*`0bc1d2bb-ce16-43a5-8b55-d3e19862f744) is required to accomplish the task but is not available from the solution
+ Other <<mscorlib.ni!ExceptionDispatchInfo.Throw>>
+ Other <<mscorlib.ni!TaskAwaiter.HandleNonSuccessAndDebuggerNotification>>
|+ microsoft.codeanalysis.workspaces.ni!Roslyn.Utilities.AsyncBatchingWorkQueue`2+<<AddWork>g__ContinueAfterDelay|15_1>d[Roslyn.Utilities.VoidResult,Roslyn.Utilities.VoidResult].MoveNext()
||+ Other <<mscorlib.ni!System.Threading.Tasks.Task.Finish(Boolean)>>
|| + microsoft.codeanalysis.workspaces.ni!System.Threading.Tasks.Task`1[Roslyn.Utilities.VoidResult].TrySetException(System.Object)
|| + microsoft.codeanalysis.workspaces.ni!System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1[Roslyn.Utilities.VoidResult].SetException(System.Exception)
|| + microsoft.codeanalysis.workspaces.ni!Roslyn.Utilities.AsyncBatchingWorkQueue`2+<ProcessNextBatchAsync>d__17[Roslyn.Utilities.VoidResult,Roslyn.Utilities.VoidResult].MoveNext()
|| + Other <<mscorlib.ni!TaskAwaiter.HandleNonSuccessAndDebuggerNotification>>
|| + microsoft.codeanalysis.workspaces.ni!Roslyn.Utilities.AsyncBatchingWorkQueue`2+<ProcessNextBatchAsync>d__17[Roslyn.Utilities.VoidResult,Roslyn.Utilities.VoidResult].MoveNext()
|| + Other <<mscorlib.ni!Task.Finish>>
|| + microsoft.codeanalysis.workspaces.ni!System.Threading.Tasks.Task`1[Roslyn.Utilities.VoidResult].TrySetException(System.Object)
|| + microsoft.codeanalysis.workspaces.ni!System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1[Roslyn.Utilities.VoidResult].SetException(System.Exception)
|| + microsoft.codeanalysis.workspaces.ni!Roslyn.Utilities.AsyncBatchingWorkQueue`1+<>c__DisplayClass2_0+<<Convert>b__0>d[Roslyn.Utilities.VoidResult].MoveNext()
|| + Other <<mscorlib.ni!TaskAwaiter.HandleNonSuccessAndDebuggerNotification>>
|| + microsoft.codeanalysis.workspaces.ni!Roslyn.Utilities.AsyncBatchingWorkQueue`1+<>c__DisplayClass2_0+<<Convert>b__0>d[Roslyn.Utilities.VoidResult].MoveNext()
|| + Other <<mscorlib.ni!System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1[System.Threading.Tasks.VoidTaskResult].SetException(System.Exception)>>
|| + system.threading.tasks.extensions.ni!?
|| + microsoft.visualstudio.languageservices.ni!Microsoft.VisualStudio.LanguageServices.Implementation.DesignerAttribute.VisualStudioDesignerAttributeService+<ProcessWorkspaceChangeAsync>d__12.MoveNext()
|| + Other <<mscorlib.ni!ExceptionDispatchInfo.Throw>>
|| + microsoft.visualstudio.languageservices.ni!Microsoft.VisualStudio.LanguageServices.Implementation.DesignerAttribute.VisualStudioDesignerAttributeService+<ProcessWorkspaceChangeAsync>d__12.MoveNext()
…esignerAttributeStream"" This reverts commit 13a67a8.
Followup to #64576.