// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Remote;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem;
using Microsoft.VisualStudio.Text;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.Remote
{
internal partial class ServiceHubRemoteHostClient
{
private class WorkspaceHost : ForegroundThreadAffinitizedObject, IVisualStudioWorkspaceHost, IVisualStudioWorkingFolder
{
private readonly VisualStudioWorkspaceImpl _workspace;
private readonly RemoteHostClient _client;
///
/// The current connection we have open to the remote host. Only accessible from the
/// UI thread.
///
private ReferenceCountedDisposable.WeakReference _currentConnection;
// We have to capture the solution ID because otherwise we won't know
// what is is when we get told about OnSolutionRemoved. If we try
// to access the solution off of _workspace at that point, it will be
// gone.
private SolutionId _currentSolutionId;
public WorkspaceHost(
VisualStudioWorkspaceImpl workspace,
RemoteHostClient client,
ReferenceCountedDisposable currentConnection)
{
_workspace = workspace;
_client = client;
_currentSolutionId = workspace.CurrentSolution.Id;
_currentConnection = new ReferenceCountedDisposable.WeakReference(currentConnection);
}
public void OnAfterWorkingFolderChange()
{
this.AssertIsForeground();
RegisterPrimarySolutionAsync().Wait();
}
public void OnSolutionAdded(SolutionInfo solutionInfo)
{
this.AssertIsForeground();
RegisterPrimarySolutionAsync().Wait();
}
private async Task> GetConnectionAsync()
{
this.AssertIsForeground();
// If we have an existing connection, add a ref to it and use that.
var connectionRef = _currentConnection.TryAddReference();
if (connectionRef == null)
{
// Otherwise, try to create an actual connection to the OOP server
var connection = await _client.TryCreateConnectionAsync(WellKnownRemoteHostServices.RemoteHostService, CancellationToken.None).ConfigureAwait(false);
if (connection == null)
{
return null;
}
// And set the ref count to it to 1.
connectionRef = new ReferenceCountedDisposable(connection);
_currentConnection = new ReferenceCountedDisposable.WeakReference(connectionRef);
}
return connectionRef;
}
private async Task RegisterPrimarySolutionAsync()
{
this.AssertIsForeground();
_currentSolutionId = _workspace.CurrentSolution.Id;
var solutionId = _currentSolutionId;
using (var connection = await GetConnectionAsync().ConfigureAwait(false))
{
if (connection == null)
{
// failed to create connection. remote host might not responding or gone.
return;
}
var storageLocation = _workspace.DeferredState?.ProjectTracker.GetWorkingFolderPath(_workspace.CurrentSolution);
await connection.Target.InvokeAsync(
nameof(IRemoteHostService.RegisterPrimarySolutionId),
new object[] { solutionId, storageLocation }, CancellationToken.None).ConfigureAwait(false);
}
}
public void OnBeforeWorkingFolderChange()
{
this.AssertIsForeground();
_currentSolutionId = _workspace.CurrentSolution.Id;
var solutionId = _currentSolutionId;
UnregisterPrimarySolutionAsync(solutionId, synchronousShutdown: true).Wait();
}
public void OnSolutionRemoved()
{
this.AssertIsForeground();
// Have to use the cached solution ID we've got as the workspace will
// no longer have a solution we can look at.
var solutionId = _currentSolutionId;
_currentSolutionId = null;
UnregisterPrimarySolutionAsync(solutionId, synchronousShutdown: false).Wait();
}
private async Task UnregisterPrimarySolutionAsync(
SolutionId solutionId, bool synchronousShutdown)
{
await _client.TryRunRemoteAsync(
WellKnownRemoteHostServices.RemoteHostService, _workspace.CurrentSolution,
nameof(IRemoteHostService.UnregisterPrimarySolutionId), new object[] { solutionId, synchronousShutdown },
CancellationToken.None).ConfigureAwait(false);
}
public void ClearSolution() { }
public void OnAdditionalDocumentAdded(DocumentInfo documentInfo) { }
public void OnAdditionalDocumentClosed(DocumentId documentId, ITextBuffer textBuffer, TextLoader loader) { }
public void OnAdditionalDocumentOpened(DocumentId documentId, ITextBuffer textBuffer, bool isCurrentContext) { }
public void OnAdditionalDocumentRemoved(DocumentId documentInfo) { }
public void OnAdditionalDocumentTextUpdatedOnDisk(DocumentId id) { }
public void OnAnalyzerReferenceAdded(ProjectId projectId, AnalyzerReference analyzerReference) { }
public void OnAnalyzerReferenceRemoved(ProjectId projectId, AnalyzerReference analyzerReference) { }
public void OnAssemblyNameChanged(ProjectId id, string assemblyName) { }
public void OnDocumentAdded(DocumentInfo documentInfo) { }
public void OnDocumentClosed(DocumentId documentId, ITextBuffer textBuffer, TextLoader loader, bool updateActiveContext) { }
public void OnDocumentOpened(DocumentId documentId, ITextBuffer textBuffer, bool isCurrentContext) { }
public void OnDocumentRemoved(DocumentId documentId) { }
public void OnDocumentTextUpdatedOnDisk(DocumentId id) { }
public void OnMetadataReferenceAdded(ProjectId projectId, PortableExecutableReference metadataReference) { }
public void OnMetadataReferenceRemoved(ProjectId projectId, PortableExecutableReference metadataReference) { }
public void OnOptionsChanged(ProjectId projectId, CompilationOptions compilationOptions, ParseOptions parseOptions) { }
public void OnOutputFilePathChanged(ProjectId id, string outputFilePath) { }
public void OnProjectAdded(ProjectInfo projectInfo) { }
public void OnProjectNameChanged(ProjectId projectId, string name, string filePath) { }
public void OnProjectReferenceAdded(ProjectId projectId, ProjectReference projectReference) { }
public void OnProjectReferenceRemoved(ProjectId projectId, ProjectReference projectReference) { }
public void OnProjectRemoved(ProjectId projectId) { }
}
}
}