Skip to content

Commit 0a0e2ce

Browse files
authored
Merge pull request #9683 from lifengl/dev/lifengl/unblockedOnProjectGuid
Allow language service to get ProjectGuid earlier in common cases.
2 parents e4f55a9 + e9e3eb6 commit 0a0e2ce

File tree

2 files changed

+48
-16
lines changed

2 files changed

+48
-16
lines changed

src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/LanguageServiceHost.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -205,16 +205,18 @@ async Task OnSlicesChangedAsync(IProjectVersionedValue<(ConfiguredProject Active
205205
// Remember the first slice's workspace. We may use it later, if the active workspace is removed.
206206
Workspace? firstWorkspace = null;
207207

208+
Guid? projectGuid = null;
209+
208210
foreach ((ProjectConfigurationSlice slice, IActiveConfigurationSubscriptionSource source) in sources)
209211
{
210212
if (!workspaceBySlice.TryGetValue(slice, out Workspace? workspace))
211213
{
212214
Assumes.False(checklist.ContainsKey(slice));
213215

214-
Guid projectGuid = await _projectGuidService.GetProjectGuidAsync(cancellationToken);
216+
projectGuid ??= await _projectGuidService.GetProjectGuidAsync(cancellationToken);
215217

216218
// New slice. Create a workspace for it.
217-
workspace = _workspaceFactory.Create(source, slice, JoinableCollection, JoinableFactory, projectGuid, cancellationToken);
219+
workspace = _workspaceFactory.Create(source, slice, JoinableCollection, JoinableFactory, projectGuid.Value, cancellationToken);
218220

219221
if (workspace is null)
220222
{
@@ -352,18 +354,16 @@ public async Task AfterLoadInitialConfigurationAsync()
352354
return;
353355
}
354356

355-
// Ensure the project is not considered loaded until our first publication.
356-
Task result = _tasksService.PrioritizedProjectLoadedInHostAsync(async () =>
357+
// prevent spin-off task to have accidental relevance
358+
using (_unconfiguredProject.Services.ThreadingPolicy.JoinableTaskContext.SuppressRelevance())
357359
{
358-
using (JoinableCollection.Join())
359-
{
360-
await WhenInitialized(_tasksService.UnloadCancellationToken);
361-
}
362-
});
360+
// Ensure the project is not considered loaded until our first publication.
361+
Task result = _tasksService.PrioritizedProjectLoadedInHostAsync(() => WhenInitialized(_tasksService.UnloadCancellationToken));
363362

364-
// While we want make sure it's loaded before PrioritizedProjectLoadedInHost,
365-
// we don't want to block project factory completion on its load, so fire and forget.
366-
_projectFaultHandler.Forget(result, _unconfiguredProject, ProjectFaultSeverity.LimitedFunctionality);
363+
// While we want make sure it's loaded before PrioritizedProjectLoadedInHost,
364+
// we don't want to block project factory completion on its load, so fire and forget.
365+
_projectFaultHandler.Forget(result, _unconfiguredProject, ProjectFaultSeverity.LimitedFunctionality);
366+
}
367367
}
368368

369369
protected override Task DisposeCoreAsync(bool initialized)

src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VsSafeProjectGuidService.cs

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the LICENSE.md file in the project root for more information.
22

3+
using Microsoft.Internal.VisualStudio.Shell.Interop;
34
using Microsoft.VisualStudio.Threading;
45

56
namespace Microsoft.VisualStudio.ProjectSystem.VS;
@@ -13,18 +14,49 @@ internal class VsSafeProjectGuidService : ISafeProjectGuidService
1314
{
1415
private readonly UnconfiguredProject _project;
1516
private readonly IUnconfiguredProjectTasksService _tasksService;
17+
private readonly IVsService<IVsBackgroundSolution> _backgroundSolutionImport;
1618

1719
[ImportingConstructor]
18-
public VsSafeProjectGuidService(UnconfiguredProject project, IUnconfiguredProjectTasksService tasksService)
20+
public VsSafeProjectGuidService(
21+
UnconfiguredProject project,
22+
IUnconfiguredProjectTasksService tasksService,
23+
IVsService<SVsBackgroundSolution, IVsBackgroundSolution> backgroundSolutionImport)
1924
{
2025
_project = project;
2126
_tasksService = tasksService;
27+
_backgroundSolutionImport = backgroundSolutionImport;
2228
}
2329

24-
public async Task<Guid> GetProjectGuidAsync(CancellationToken cancellationToken = default)
30+
public Task<Guid> GetProjectGuidAsync(CancellationToken cancellationToken = default)
2531
{
26-
await _tasksService.PrioritizedProjectLoadedInHost.WithCancellation(cancellationToken);
32+
if (!_tasksService.PrioritizedProjectLoadedInHost.IsCompleted)
33+
{
34+
return GetProjectGuidSlowAsync(cancellationToken);
35+
}
2736

28-
return await _project.GetProjectGuidAsync();
37+
return _project.GetProjectGuidAsync();
38+
39+
async Task<Guid> GetProjectGuidSlowAsync(CancellationToken cancellationToken)
40+
{
41+
Guid projectGuid = await _project.GetProjectGuidAsync();
42+
43+
// if this is a newly created project, the project GUID might be empty.
44+
// we should wait until the project is added to the solution.
45+
if (projectGuid != Guid.Empty)
46+
{
47+
// now, get the GUID recorded by the solution.
48+
// the solution might not know the project, if this is a project just to be added to the solution.
49+
// or if the project saves a different GUID, we need wait the solution to resolve this conflict.
50+
IVsBackgroundSolution solution = await _backgroundSolutionImport.GetValueAsync(cancellationToken);
51+
if (solution.GetProjectGuidFromAbsolutePath(_project.FullPath) == projectGuid)
52+
{
53+
// If the project GUID matches the one from the solution, we can return it immediately.
54+
return projectGuid;
55+
}
56+
}
57+
58+
await _tasksService.PrioritizedProjectLoadedInHost.WithCancellation(cancellationToken);
59+
return await _project.GetProjectGuidAsync();
60+
}
2961
}
3062
}

0 commit comments

Comments
 (0)