提交 e6a43cb5 编写于 作者: K Kevin Pilch

Move IVsSolutionEvents to VisualStudioWorkspaceImpl

In Lightweight solution load, we depend on
IVsSolutionEvents.OnAfterOpenSolution being called to populate the
workspace and create projects.  However we were subscribing from
VisualStudioProjectTracker, which we defer create until a project is
opened.  This meant we never got the event for the first time a solution
was opened, and so we failed to populate the workspace.
上级 a1b47dbf
......@@ -75,6 +75,8 @@ protected override void Initialize()
// start remote host
EnableRemoteHostClientService();
Workspace.AdviseSolutionEvents(GetService(typeof(SVsSolution)) as IVsSolution);
}
// Ensure services that must be created on the UI thread have been.
......
......@@ -55,8 +55,6 @@ internal sealed partial class VisualStudioProjectTracker : ForegroundThreadAffin
/// being pushed to a particular host if <see cref="WorkspaceHostState.HostReadyForEvents"/> isn't true yet.
/// </summary>
private bool _solutionLoadComplete = false;
private uint? _solutionEventsCookie;
#endregion
#region Mutable fields accessed from foreground or background threads - need locking for access.
......@@ -113,8 +111,6 @@ public VisualStudioProjectTracker(IServiceProvider serviceProvider, HostWorkspac
_vsSolution = (IVsSolution)serviceProvider.GetService(typeof(SVsSolution));
_runningDocumentTable = (IVsRunningDocumentTable4)serviceProvider.GetService(typeof(SVsRunningDocumentTable));
_vsSolution.AdviseSolutionEvents(this, out var solutionEventsCookie);
_solutionEventsCookie = solutionEventsCookie;
// It's possible that we're loading after the solution has already fully loaded, so see if we missed the event
var shellMonitorSelection = (IVsMonitorSelection)serviceProvider.GetService(typeof(SVsShellMonitorSelection));
......@@ -226,12 +222,6 @@ public void InitializeProviders(DocumentProvider documentProvider, VisualStudioM
public void Dispose()
{
if (_solutionEventsCookie.HasValue)
{
_vsSolution.UnadviseSolutionEvents(_solutionEventsCookie.Value);
_solutionEventsCookie = null;
}
if (this.RuleSetFileProvider != null)
{
this.RuleSetFileProvider.Dispose();
......@@ -495,7 +485,7 @@ internal void TryDisconnectExistingDeferredProject(IVsHierarchy hierarchy, strin
// If we created a project for this while in deferred project load mode, let's close it
// now that we're being asked to make a "real" project for it, so that we'll prefer the
// "real" project
if (IsDeferredSolutionLoadEnabled())
if (VisualStudioWorkspaceImpl.IsDeferredSolutionLoadEnabled(_serviceProvider))
{
var existingProject = GetProject(projectId);
if (existingProject != null)
......@@ -506,12 +496,58 @@ internal void TryDisconnectExistingDeferredProject(IVsHierarchy hierarchy, strin
}
}
private bool IsDeferredSolutionLoadEnabled()
public void OnBeforeCloseSolution()
{
// NOTE: It is expected that the "as" will fail on Dev14, as IVsSolution7 was
// introduced in Dev15. Be sure to handle the null result here.
var solution7 = _serviceProvider.GetService(typeof(SVsSolution)) as IVsSolution7;
return solution7?.IsSolutionLoadDeferred() == true;
AssertIsForeground();
_solutionIsClosing = true;
foreach (var p in this.ImmutableProjects)
{
p.StopPushingToWorkspaceHosts();
}
_solutionLoadComplete = false;
// Cancel any background solution parsing. NOTE: This means that work needs to
// check the token periodically, and whenever resuming from an "await"
_solutionParsingCancellationTokenSource.Cancel();
_solutionParsingCancellationTokenSource = new CancellationTokenSource();
}
public void OnAfterCloseSolution()
{
AssertIsForeground();
if (VisualStudioWorkspaceImpl.IsDeferredSolutionLoadEnabled(_serviceProvider))
{
// Copy to avoid modifying the collection while enumerating
var loadedProjects = ImmutableProjects.ToList();
foreach (var p in loadedProjects)
{
p.Disconnect();
}
}
lock (_gate)
{
Contract.ThrowIfFalse(_projectMap.Count == 0);
}
NotifyWorkspaceHosts(host => host.OnSolutionRemoved());
NotifyWorkspaceHosts(host => host.ClearSolution());
lock (_gate)
{
_projectPathToIdMap.Clear();
}
foreach (var workspaceHost in _workspaceHosts)
{
workspaceHost.SolutionClosed();
}
_solutionIsClosing = false;
}
}
}
......@@ -33,14 +33,13 @@ int IVsSolutionLoadEvents.OnBeforeOpenSolution(string pszSolutionFilename)
return VSConstants.S_OK;
}
private async Task LoadSolutionFromMSBuildAsync(
CancellationToken cancellationToken)
public async Task LoadSolutionFromMSBuildAsync()
{
AssertIsForeground();
InitializeOutputPane();
// Continue on the UI thread for these operations, since we are touching the VisualStudioWorkspace, etc.
await PopulateWorkspaceFromDeferredProjectInfoAsync(cancellationToken).ConfigureAwait(true);
await PopulateWorkspaceFromDeferredProjectInfoAsync(_solutionParsingCancellationTokenSource.Token).ConfigureAwait(true);
}
[Conditional("DEBUG")]
......
......@@ -1022,6 +1022,12 @@ protected override void Dispose(bool finalize)
// workspace is going away. unregister this workspace from work coordinator
StopSolutionCrawler();
// We should consider calling this here. It is commented out because Solution event tracking was
// moved from VisualStudioProjectTracker, which is never Dispose()'d. Rather than risk the
// UnadviseSolutionEvents causing another issue (calling into dead COM objects, etc), we'll just
// continue to skip it for now.
// UnadviseSolutionEvents();
base.Dispose(finalize);
}
......
......@@ -5,11 +5,32 @@
using Microsoft.VisualStudio.Shell.Interop;
using Roslyn.Utilities;
using Microsoft.CodeAnalysis.Host;
using System;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.Interop;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{
internal partial class VisualStudioProjectTracker : IVsSolutionEvents
internal partial class VisualStudioWorkspaceImpl : IVsSolutionEvents
{
private IVsSolution _vsSolution;
private uint? _solutionEventsCookie;
public void AdviseSolutionEvents(IVsSolution solution)
{
_vsSolution = solution;
_vsSolution.AdviseSolutionEvents(this, out var solutionEventsCookie);
_solutionEventsCookie = solutionEventsCookie;
}
public void UnadviseSolutionEvents()
{
if (_solutionEventsCookie.HasValue)
{
_vsSolution.UnadviseSolutionEvents(_solutionEventsCookie.Value);
_solutionEventsCookie = null;
}
}
int IVsSolutionEvents.OnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy)
{
return VSConstants.S_OK;
......@@ -22,11 +43,11 @@ int IVsSolutionEvents.OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded)
int IVsSolutionEvents.OnAfterOpenSolution(object pUnkReserved, int fNewSolution)
{
AssertIsForeground();
_foregroundObject.Value.AssertIsForeground();
if (IsDeferredSolutionLoadEnabled())
if (IsDeferredSolutionLoadEnabled(Shell.ServiceProvider.GlobalProvider))
{
LoadSolutionFromMSBuildAsync(_solutionParsingCancellationTokenSource.Token).FireAndForget();
GetProjectTrackerAndInitializeIfNecessary(Shell.ServiceProvider.GlobalProvider).LoadSolutionFromMSBuildAsync().FireAndForget();
}
return VSConstants.S_OK;
......@@ -59,60 +80,23 @@ int IVsSolutionEvents.OnQueryCloseSolution(object pUnkReserved, ref int pfCancel
int IVsSolutionEvents.OnBeforeCloseSolution(object pUnkReserved)
{
AssertIsForeground();
_solutionIsClosing = true;
foreach (var p in this.ImmutableProjects)
{
p.StopPushingToWorkspaceHosts();
}
_solutionLoadComplete = false;
// Cancel any background solution parsing. NOTE: This means that that work needs to
// check the token periodically, and whenever resuming from an "await"
_solutionParsingCancellationTokenSource.Cancel();
_solutionParsingCancellationTokenSource = new CancellationTokenSource();
DeferredState?.ProjectTracker.OnBeforeCloseSolution();
return VSConstants.S_OK;
}
int IVsSolutionEvents.OnAfterCloseSolution(object pUnkReserved)
{
AssertIsForeground();
if (IsDeferredSolutionLoadEnabled())
{
// Copy to avoid modifying the collection while enumerating
var loadedProjects = ImmutableProjects.ToList();
foreach (var p in loadedProjects)
{
p.Disconnect();
}
}
lock (_gate)
{
Contract.ThrowIfFalse(_projectMap.Count == 0);
}
NotifyWorkspaceHosts(host => host.OnSolutionRemoved());
NotifyWorkspaceHosts(host => host.ClearSolution());
lock (_gate)
{
_projectPathToIdMap.Clear();
}
foreach (var workspaceHost in _workspaceHosts)
{
workspaceHost.SolutionClosed();
}
_solutionIsClosing = false;
DeferredState?.ProjectTracker.OnAfterCloseSolution();
return VSConstants.S_OK;
}
internal static bool IsDeferredSolutionLoadEnabled(IServiceProvider serviceProvider)
{
// NOTE: It is expected that the "as" will fail on Dev14, as IVsSolution7 was
// introduced in Dev15. Be sure to handle the null result here.
var solution7 = serviceProvider.GetService(typeof(SVsSolution)) as IVsSolution7;
return solution7?.IsSolutionLoadDeferred() == true;
}
}
}
......@@ -549,7 +549,7 @@
<Compile Include="Implementation\ProjectSystem\VisualStudioProjectManagementService.cs" />
<Compile Include="Implementation\ProjectSystem\VisualStudioProjectTracker.cs" />
<Compile Include="Implementation\ProjectSystem\VisualStudioProjectTracker.WorkspaceHostState.cs" />
<Compile Include="Implementation\ProjectSystem\VisualStudioProjectTracker_IVsSolutionEvents.cs" />
<Compile Include="Implementation\ProjectSystem\VisualStudioWorkspaceImpl_IVsSolutionEvents.cs" />
<Compile Include="Implementation\ProjectSystem\VisualStudioProjectTracker_IVsSolutionLoadEvents.cs" />
<Compile Include="Implementation\ProjectSystem\VisualStudioWorkspace.cs" />
<Compile Include="Implementation\ProjectSystem\VisualStudioWorkspaceImpl.cs" />
......@@ -704,4 +704,4 @@
</ItemGroup>
<Import Project="..\..\..\Dependencies\CodeAnalysis.Metadata\Microsoft.CodeAnalysis.Metadata.projitems" Label="Shared" />
<Import Project="..\..\..\..\build\Targets\Imports.targets" />
</Project>
</Project>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册