Skip to content

[Proposal?] Update the PM UI when a successful restore finishes#7209

Open
martinrrm wants to merge 5 commits intodevfrom
dev-martinrrm-refresh-pmui-on-restore
Open

[Proposal?] Update the PM UI when a successful restore finishes#7209
martinrrm wants to merge 5 commits intodevfrom
dev-martinrrm-refresh-pmui-on-restore

Conversation

@martinrrm
Copy link
Copy Markdown
Contributor

@martinrrm martinrrm commented Mar 12, 2026

Bug

Fixes: NuGet/Home#14761

Description

This is a proposal fix for NuGet/Home#14761, currently we don't directly watch for files saves, instead we do something like this:

If the file save causes CPS/project system nomination, the path is:

  1. VS project system detects project/props change
  2. project system nominates restore
  3. restore nomination reaches VsSolutionRestoreService
    4. AddProjectRestoreInfo(...) updates ProjectSystemCache
  4. CacheUpdated fires
  5. VSSolutionManager.AfterNuGetCacheUpdated fires
  6. PM UI refreshes through OnNuGetCacheUpdated
image

Refresh PM UI on restore completion instead of cache nomination

The Package Manager UI previously refreshed via AfterNuGetCacheUpdated, which fires during nomination (pre-restore) — meaning the UI would refresh with stale data
before the restore had actually written updated assets files.

This PR replaces that mechanism with IVsNuGetProjectUpdateEvents, which fires after restore completes and assets files are written:

  • ProjectUpdateFinished — per-project, post-restore signal. Only fires for non-no-op restores (guarded by !isNoOp in RestoreRunner.CommitAsync). Also fires for
    packages.config install/uninstall via PackageProjectEvents.BatchEnd.
  • SolutionRestoreFinished — solution-level, post-restore signal. Used for solution-level PM UI, but only triggers a refresh if at least one ProjectUpdateFinished
    fired during that restore cycle (skips full no-op restores).

Key changes

File Change
PackageManagerControl.xaml.cs Replace AfterNuGetCacheUpdated subscription with IVsNuGetProjectUpdateEvents.ProjectUpdateFinished and SolutionRestoreFinished
PackageManagerControl.xaml.cs Add _projectUpdateOccurredDuringRestore flag so solution-level PM UI skips refresh on full no-op restores
PackageManagerControl.xaml.cs Add deferred refresh: when PM UI is not visible during restore, set _isRefreshRequired = true and refresh on next Loaded event
PackageManagerUIRefreshEvent.cs Add RestoreCompleted to RefreshOperationSource telemetry enum
VsRestoreProgressEventsTests.cs Add 4 tests: exception isolation and multi-subscriber support for EndProjectUpdate and EndSolutionRestore
NuGetTelemetryServiceTests.cs Add 3 telemetry test cases for RestoreCompleted (Success, NoOp, NotApplicable)

Behavior summary

Scenario Old (CacheUpdated) New (ProjectUpdateFinished)
PackageReference restore (non-no-op) ✅ but stale (pre-restore) ✅ fresh (post-restore)
PackageReference restore (no-op) ✅ fires anyway ❌ skipped — no unnecessary refresh
packages.config install/uninstall ❌ not fired ✅ covered via BatchEnd
Solution full no-op restore ✅ fires anyway ❌ skipped via _projectUpdateOccurredDuringRestore
PM UI hidden during restore Ignored, stale on return Deferred — _isRefreshRequired triggers refresh on Loaded

How it works

  • Project-level PM UI: OnProjectUpdateFinished filters by matching projectUniqueName against the viewed project. Refreshes immediately if visible, defers if
    hidden.
  • Solution-level PM UI: OnProjectUpdateFinished sets _projectUpdateOccurredDuringRestore = true. OnSolutionRestoreFinished checks this flag — only refreshes if
    at least one project had a real restore. This avoids both N-per-project refreshes and unnecessary refreshes on full no-op restores.
  • During install/uninstall actions: _isExecutingAction gates all refresh signals; single refresh happens in ExecuteAction finally block (no double refresh).

PR Checklist

  • Meaningful title, helpful description and a linked NuGet/Home issue
  • Added tests
  • Link to an issue or pull request to update docs if this PR changes settings, environment variables, new feature, etc.

@martinrrm martinrrm requested a review from a team as a code owner March 12, 2026 19:08
_service.ProjectRemoved += OnProjectRemoved;
_service.ProjectRenamed += OnProjectRenamed;
_service.ProjectUpdated += OnProjectUpdated;
_service.SolutionRestoreCompleted += OnSolutionRestoreCompleted;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

So AfterNuGetCacheUpdated is tied to nomination.

Is AfterNuGetCacheUpdated not firing?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yep is not firing, not sure why, I'll investigate more since doesn't make a lot of sense to me

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@NuGet/nuget-client Okay second investigation help me understand better the problem:

There are two main problems in the PM UI:

  1. We refresh when nomination fires, not when restore completes, so it could happen that PM UI is not up to date with the project state.
  2. We only refresh the PM UI if it's visible, we have a comment saying that it will refresh when we focus on it again, but that code is not working, it's a simple fix

Most likely what is happening is that Copilot Chat switches focus to the csproj file and PM UI is not being restored, I was able to reproduce this behavior, but also Copilot doesn't always focus to the file.

I was also able to reproduce the "late" restore problem, most likely because I was debugging.

I think adding a way to restore the PM UI when restore finishes is needed but could be more intrusive change.

Copy link
Copy Markdown
Member

@nkolev92 nkolev92 Mar 13, 2026

Choose a reason for hiding this comment

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

I think adding a way to restore the PM UI when restore finishes is needed but could be more intrusive change

Yeah, let's delay that, but there's already some APIs that are intelligent about when a project has been updated, so if we do that, we should be able to only do restore updates when needed.

If we add a simple restore refresh, I think it'll refresh twice when package installation happens.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I changed the implementation, I added two new event handlers in the PM UI, ProjectUpdateFinished and SolutionRestoreFinished, this is the flow of each event:

Per-project: RestoreRunner.CommitAsync calls EndProjectUpdate → fires ProjectUpdateFinished once per project, immediately after that project's assets files are written.
Guarded by !isNoOp — only fires when something actually changed.

Solution-level: SolutionRestoreJob calls EndSolutionRestore → fires SolutionRestoreFinished once at the very end, after ALL projects have been committed (including
no-ops).

I had to add the SolutionRestoreFinished to avoid N project refreshes in the PM UI, but by combining both we can handle no-op restores in the Solution level

Copy link
Copy Markdown
Contributor

@jebriede jebriede left a comment

Choose a reason for hiding this comment

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

@martinrrm it looks like you're going to explore refreshing when a project has been updated instead of when the restore completes. Please request rereviews and update the PR description when that's ready. Thanks!

@martinrrm martinrrm requested a review from nkolev92 March 23, 2026 21:38
Use _projectUpdateOccurredDuringRestore flag to track whether any
project had a non-no-op restore. Solution-level PM UI only refreshes
in OnSolutionRestoreFinished if the flag is set, avoiding unnecessary
refreshes when the entire restore was a no-op.

Also reorder OnProjectUpdateFinished to check Model.IsSolution first,
setting the flag for solution-level PM UI before checking visibility.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@martinrrm martinrrm requested a review from jebriede March 23, 2026 21:39
@martinrrm martinrrm force-pushed the dev-martinrrm-refresh-pmui-on-restore branch from ad08b79 to 2b6ba07 Compare March 23, 2026 22:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug Bash][Unstable] The ‘Installed’ tab was not refreshed after fixing the NuGet package vulnerabilities with GitHub Copilot

3 participants