зеркало из https://github.com/dotnet/razor.git
Add Microsoft.VisualStudio.LiveShare.Razor
- Added LiveShare to the RazorExtension - Updated dependencies to consume LiveShare nupkg aspnet/Razor.LiveShare#47
This commit is contained in:
Родитель
f771e72108
Коммит
599c81f948
|
@ -68,6 +68,7 @@
|
|||
<MicrosoftVisualStudioEditorPackageVersion>16.0.177-g0ae5fa46a1</MicrosoftVisualStudioEditorPackageVersion>
|
||||
<MicrosoftVisualStudioLanguagePackageVersion>16.0.177-g0ae5fa46a1</MicrosoftVisualStudioLanguagePackageVersion>
|
||||
<MicrosoftVisualStudioLanguageIntellisensePackageVersion>16.0.177-g0ae5fa46a1</MicrosoftVisualStudioLanguageIntellisensePackageVersion>
|
||||
<MicrosoftVisualLiveSharePackageVersion>0.3.959</MicrosoftVisualLiveSharePackageVersion>
|
||||
<MicrosoftVisualStudioOLEInteropPackageVersion>7.10.6071</MicrosoftVisualStudioOLEInteropPackageVersion>
|
||||
<MicrosoftVisualStudioProjectSystemAnalyzersPackageVersion>16.0.201-pre-g7d366164d0</MicrosoftVisualStudioProjectSystemAnalyzersPackageVersion>
|
||||
<MicrosoftVisualStudioProjectSystemManagedVSPackageVersion>3.0.0-beta1-63529-03</MicrosoftVisualStudioProjectSystemManagedVSPackageVersion>
|
||||
|
|
|
@ -82,6 +82,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Mac.
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.Test.ComponentShim", "test\Microsoft.AspNetCore.Razor.Test.ComponentShim\Microsoft.AspNetCore.Razor.Test.ComponentShim.csproj", "{A9FDA40B-0EE4-4B00-8E24-F1C8B62154BE}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.VisualStudio.LiveShare.Razor", "src\Microsoft.VisualStudio.LiveShare.Razor\Microsoft.VisualStudio.LiveShare.Razor.csproj", "{CD06AFC8-81C6-4A26-A818-072AB2C44E8C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.VisualStudio.LiveShare.Razor.Test", "test\Microsoft.VisualStudio.LiveShare.Razor.Test\Microsoft.VisualStudio.LiveShare.Razor.Test.csproj", "{0C7B8131-1150-4345-837F-6F6AD2CAE606}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -228,6 +232,14 @@ Global
|
|||
{A9FDA40B-0EE4-4B00-8E24-F1C8B62154BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A9FDA40B-0EE4-4B00-8E24-F1C8B62154BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A9FDA40B-0EE4-4B00-8E24-F1C8B62154BE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CD06AFC8-81C6-4A26-A818-072AB2C44E8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CD06AFC8-81C6-4A26-A818-072AB2C44E8C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CD06AFC8-81C6-4A26-A818-072AB2C44E8C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CD06AFC8-81C6-4A26-A818-072AB2C44E8C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0C7B8131-1150-4345-837F-6F6AD2CAE606}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0C7B8131-1150-4345-837F-6F6AD2CAE606}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0C7B8131-1150-4345-837F-6F6AD2CAE606}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0C7B8131-1150-4345-837F-6F6AD2CAE606}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -268,6 +280,8 @@ Global
|
|||
{D87E5501-B832-46B6-ACD3-EC989E3D14ED} = {92463391-81BE-462B-AC3C-78C6C760741F}
|
||||
{A34CCC12-687B-4D12-AA0E-F5BE800DE19C} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
|
||||
{A9FDA40B-0EE4-4B00-8E24-F1C8B62154BE} = {92463391-81BE-462B-AC3C-78C6C760741F}
|
||||
{CD06AFC8-81C6-4A26-A818-072AB2C44E8C} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
|
||||
{0C7B8131-1150-4345-837F-6F6AD2CAE606} = {92463391-81BE-462B-AC3C-78C6C760741F}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {0035341D-175A-4D05-95E6-F1C2785A1E26}
|
||||
|
|
|
@ -86,6 +86,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Mac.
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.Test.ComponentShim", "test\Microsoft.AspNetCore.Razor.Test.ComponentShim\Microsoft.AspNetCore.Razor.Test.ComponentShim.csproj", "{5B232E77-F0D3-4298-9A5D-D965788D7A79}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.VisualStudio.LiveShare.Razor", "src\Microsoft.VisualStudio.LiveShare.Razor\Microsoft.VisualStudio.LiveShare.Razor.csproj", "{20193C6A-8981-447F-99B3-120DD3B06279}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.VisualStudio.LiveShare.Razor.Test", "test\Microsoft.VisualStudio.LiveShare.Razor.Test\Microsoft.VisualStudio.LiveShare.Razor.Test.csproj", "{9A27DD55-E8CD-4C03-A89B-A7348B787660}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -390,6 +394,22 @@ Global
|
|||
{5B232E77-F0D3-4298-9A5D-D965788D7A79}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5B232E77-F0D3-4298-9A5D-D965788D7A79}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5B232E77-F0D3-4298-9A5D-D965788D7A79}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
|
||||
{20193C6A-8981-447F-99B3-120DD3B06279}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{20193C6A-8981-447F-99B3-120DD3B06279}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{20193C6A-8981-447F-99B3-120DD3B06279}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{20193C6A-8981-447F-99B3-120DD3B06279}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
|
||||
{20193C6A-8981-447F-99B3-120DD3B06279}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{20193C6A-8981-447F-99B3-120DD3B06279}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{20193C6A-8981-447F-99B3-120DD3B06279}.ReleaseNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{20193C6A-8981-447F-99B3-120DD3B06279}.ReleaseNoVSIX|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9A27DD55-E8CD-4C03-A89B-A7348B787660}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9A27DD55-E8CD-4C03-A89B-A7348B787660}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9A27DD55-E8CD-4C03-A89B-A7348B787660}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9A27DD55-E8CD-4C03-A89B-A7348B787660}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9A27DD55-E8CD-4C03-A89B-A7348B787660}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9A27DD55-E8CD-4C03-A89B-A7348B787660}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9A27DD55-E8CD-4C03-A89B-A7348B787660}.ReleaseNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9A27DD55-E8CD-4C03-A89B-A7348B787660}.ReleaseNoVSIX|Any CPU.Build.0 = Debug|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -432,6 +452,8 @@ Global
|
|||
{23424C44-A86D-4DBD-9274-E358B19DDBA1} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
|
||||
{A34CCC12-687B-4D12-AA0E-F5BE800DE19C} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
|
||||
{5B232E77-F0D3-4298-9A5D-D965788D7A79} = {92463391-81BE-462B-AC3C-78C6C760741F}
|
||||
{20193C6A-8981-447F-99B3-120DD3B06279} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
|
||||
{9A27DD55-E8CD-4C03-A89B-A7348B787660} = {92463391-81BE-462B-AC3C-78C6C760741F}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {0035341D-175A-4D05-95E6-F1C2785A1E26}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor
|
||||
{
|
||||
internal static class Constants
|
||||
{
|
||||
internal const string GuestOnlyWorkspaceLayer = "AnyCodeRoslynWorkspace";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Guest
|
||||
{
|
||||
internal class DefaultProjectSnapshotHandleStore : ProjectSnapshotHandleStore
|
||||
{
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
private readonly JoinableTaskFactory _joinableTaskFactory;
|
||||
private readonly ProxyAccessor _proxyAccessor;
|
||||
|
||||
// Internal for testing
|
||||
internal JoinableTask TestInitializationTask;
|
||||
|
||||
private IReadOnlyList<ProjectSnapshotHandleProxy> _projectHandles;
|
||||
private bool _triggerChangeAfterInitialize;
|
||||
|
||||
public DefaultProjectSnapshotHandleStore(
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
JoinableTaskFactory joinableTaskFactory,
|
||||
ProxyAccessor proxyAccessor)
|
||||
{
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
if (joinableTaskFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(joinableTaskFactory));
|
||||
}
|
||||
|
||||
if (proxyAccessor == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(proxyAccessor));
|
||||
}
|
||||
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
_joinableTaskFactory = joinableTaskFactory;
|
||||
_proxyAccessor = proxyAccessor;
|
||||
}
|
||||
|
||||
public override event EventHandler<ProjectProxyChangeEventArgs> Changed;
|
||||
|
||||
private bool Initialized => _projectHandles != null;
|
||||
|
||||
public override IReadOnlyList<ProjectSnapshotHandleProxy> GetProjectHandles()
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
if (!Initialized)
|
||||
{
|
||||
// Projects requested before initialization
|
||||
_triggerChangeAfterInitialize = true;
|
||||
|
||||
return Array.Empty<ProjectSnapshotHandleProxy>();
|
||||
}
|
||||
|
||||
return _projectHandles;
|
||||
}
|
||||
|
||||
public void InitializeProjects()
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
// We wire the changed event up early because any changed events that fire will ensure we have the most
|
||||
// up-to-date state.
|
||||
var snapshotManagerProxy = _proxyAccessor.GetProjectSnapshotManagerProxy();
|
||||
snapshotManagerProxy.Changed += HostProxyStateManager_Changed;
|
||||
|
||||
TestInitializationTask = _joinableTaskFactory.RunAsync(async () =>
|
||||
{
|
||||
var state = await snapshotManagerProxy.GetStateAsync(CancellationToken.None);
|
||||
|
||||
await _joinableTaskFactory.SwitchToMainThreadAsync();
|
||||
|
||||
if (Initialized)
|
||||
{
|
||||
// State was initialized from the changed event firing, that state will be more up-to-date than this.
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateProjects(state);
|
||||
|
||||
if (_triggerChangeAfterInitialize)
|
||||
{
|
||||
// Someone requested the set of projects prior to us being initialized. Let listeners know that projects
|
||||
// have been added. This way we don't have to block on the foreground thread for initialization; however,
|
||||
// this also assumes that anyone listening re-computes their state when we trigger project changes
|
||||
// (a correct assumption).
|
||||
|
||||
for (var i = 0; i < _projectHandles.Count; i++)
|
||||
{
|
||||
var args = new ProjectProxyChangeEventArgs(_projectHandles[i].FilePath, ProjectProxyChangeKind.ProjectAdded);
|
||||
OnChanged(args);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal async void HostProxyStateManager_Changed(object sender, ProjectManagerProxyChangeEventArgs args)
|
||||
{
|
||||
if (args == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(args));
|
||||
}
|
||||
|
||||
if (_foregroundDispatcher.IsForegroundThread)
|
||||
{
|
||||
UpdateProjectsAndTriggerChangeForeground();
|
||||
}
|
||||
else
|
||||
{
|
||||
await _joinableTaskFactory.SwitchToMainThreadAsync();
|
||||
UpdateProjectsAndTriggerChangeForeground();
|
||||
}
|
||||
|
||||
void UpdateProjectsAndTriggerChangeForeground()
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
UpdateProjects(args.State);
|
||||
|
||||
// We will have already triggered change events to ensure listeners are up-to-date.
|
||||
_triggerChangeAfterInitialize = false;
|
||||
|
||||
OnChanged(args.Change);
|
||||
}
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal void UpdateProjects(ProjectSnapshotManagerProxyState state)
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
_projectHandles = state.ProjectHandles;
|
||||
}
|
||||
|
||||
private void OnChanged(ProjectProxyChangeEventArgs args)
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
Changed?.Invoke(this, args);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Composition;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Host.Mef;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Guest
|
||||
{
|
||||
[ExportLanguageServiceFactory(typeof(ProjectSnapshotHandleStore), RazorLanguage.Name, Constants.GuestOnlyWorkspaceLayer)]
|
||||
internal class DefaultProjectSnapshotHandleStoreFactory : ILanguageServiceFactory
|
||||
{
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
private readonly JoinableTaskContext _joinableTaskContext;
|
||||
|
||||
[ImportingConstructor]
|
||||
public DefaultProjectSnapshotHandleStoreFactory(
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
JoinableTaskContext joinableTaskContext)
|
||||
{
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
if (joinableTaskContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(joinableTaskContext));
|
||||
}
|
||||
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
_joinableTaskContext = joinableTaskContext;
|
||||
}
|
||||
|
||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
||||
{
|
||||
if (languageServices == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(languageServices));
|
||||
}
|
||||
|
||||
var proxyAccessor = languageServices.GetRequiredService<ProxyAccessor>();
|
||||
|
||||
var snapshotStore = new DefaultProjectSnapshotHandleStore(
|
||||
_foregroundDispatcher,
|
||||
_joinableTaskContext.Factory,
|
||||
proxyAccessor);
|
||||
snapshotStore.InitializeProjects();
|
||||
|
||||
return snapshotStore;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Guest
|
||||
{
|
||||
public class DefaultProxyAccessor : ProxyAccessor
|
||||
{
|
||||
private readonly LiveShareClientProvider _liveShareClientProvider;
|
||||
private readonly JoinableTaskFactory _joinableTaskFactory;
|
||||
private IProjectSnapshotManagerProxy _projectSnapshotManagerProxy;
|
||||
private IProjectHierarchyProxy _projectHierarchyProxy;
|
||||
|
||||
public DefaultProxyAccessor(
|
||||
LiveShareClientProvider liveShareClientProvider,
|
||||
JoinableTaskFactory joinableTaskFactory)
|
||||
{
|
||||
if (liveShareClientProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(liveShareClientProvider));
|
||||
}
|
||||
|
||||
if (joinableTaskFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(joinableTaskFactory));
|
||||
}
|
||||
|
||||
_liveShareClientProvider = liveShareClientProvider;
|
||||
_joinableTaskFactory = joinableTaskFactory;
|
||||
}
|
||||
|
||||
// Testing constructor
|
||||
private protected DefaultProxyAccessor()
|
||||
{
|
||||
}
|
||||
|
||||
public override IProjectSnapshotManagerProxy GetProjectSnapshotManagerProxy()
|
||||
{
|
||||
if (_projectSnapshotManagerProxy == null)
|
||||
{
|
||||
_projectSnapshotManagerProxy = CreateServiceProxy<IProjectSnapshotManagerProxy>();
|
||||
}
|
||||
|
||||
return _projectSnapshotManagerProxy;
|
||||
}
|
||||
|
||||
public override IProjectHierarchyProxy GetProjectHierarchyProxy()
|
||||
{
|
||||
if (_projectHierarchyProxy == null)
|
||||
{
|
||||
_projectHierarchyProxy = CreateServiceProxy<IProjectHierarchyProxy>();
|
||||
}
|
||||
|
||||
return _projectHierarchyProxy;
|
||||
}
|
||||
|
||||
// Internal virtual for testing
|
||||
internal virtual TProxy CreateServiceProxy<TProxy>() where TProxy : class
|
||||
{
|
||||
return _joinableTaskFactory.Run(() => _liveShareClientProvider.CreateServiceProxyAsync<TProxy>());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Composition;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Host.Mef;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Guest
|
||||
{
|
||||
[Shared]
|
||||
[ExportLanguageServiceFactory(typeof(ProxyAccessor), RazorLanguage.Name, Constants.GuestOnlyWorkspaceLayer)]
|
||||
public class DefaultProxyAccessorFactory : ILanguageServiceFactory
|
||||
{
|
||||
private readonly LiveShareClientProvider _liveShareClientProvider;
|
||||
private readonly JoinableTaskContext _joinableTaskContext;
|
||||
|
||||
[ImportingConstructor]
|
||||
public DefaultProxyAccessorFactory(
|
||||
LiveShareClientProvider liveShareClientProvider,
|
||||
JoinableTaskContext joinableTaskContext)
|
||||
{
|
||||
if (liveShareClientProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(liveShareClientProvider));
|
||||
}
|
||||
|
||||
if (joinableTaskContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(joinableTaskContext));
|
||||
}
|
||||
|
||||
_liveShareClientProvider = liveShareClientProvider;
|
||||
_joinableTaskContext = joinableTaskContext;
|
||||
}
|
||||
|
||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
||||
{
|
||||
if (languageServices == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(languageServices));
|
||||
}
|
||||
|
||||
var proxyAccessor = new DefaultProxyAccessor(_liveShareClientProvider, _joinableTaskContext.Factory);
|
||||
return proxyAccessor;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.VisualStudio.Editor.Razor;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Guest
|
||||
{
|
||||
internal class GuestProjectPathProvider : ProjectPathProvider
|
||||
{
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
private readonly JoinableTaskFactory _joinableTaskFactory;
|
||||
private readonly ITextDocumentFactoryService _textDocumentFactory;
|
||||
private readonly ProxyAccessor _proxyAccessor;
|
||||
private readonly LiveShareClientProvider _liveShareClientProvider;
|
||||
|
||||
public GuestProjectPathProvider(
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
JoinableTaskFactory joinableTaskFactory,
|
||||
ITextDocumentFactoryService textDocumentFactory,
|
||||
ProxyAccessor proxyAccessor,
|
||||
LiveShareClientProvider liveShareClientProvider)
|
||||
{
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
if (joinableTaskFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(joinableTaskFactory));
|
||||
}
|
||||
|
||||
if (textDocumentFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(textDocumentFactory));
|
||||
}
|
||||
|
||||
if (proxyAccessor == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(proxyAccessor));
|
||||
}
|
||||
|
||||
if (liveShareClientProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(liveShareClientProvider));
|
||||
}
|
||||
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
_joinableTaskFactory = joinableTaskFactory;
|
||||
_textDocumentFactory = textDocumentFactory;
|
||||
_proxyAccessor = proxyAccessor;
|
||||
_liveShareClientProvider = liveShareClientProvider;
|
||||
}
|
||||
|
||||
public override bool TryGetProjectPath(ITextBuffer textBuffer, out string filePath)
|
||||
{
|
||||
if (!_textDocumentFactory.TryGetTextDocument(textBuffer, out var textDocument))
|
||||
{
|
||||
filePath = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var hostProjectPath = GetHostProjectPath(textDocument);
|
||||
if (hostProjectPath == null)
|
||||
{
|
||||
filePath = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Host always responds with a host-based path, convert back to a guest one.
|
||||
filePath = _liveShareClientProvider.ConvertToLocalPath(hostProjectPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Internal virtual for testing
|
||||
internal virtual Uri GetHostProjectPath(ITextDocument textDocument)
|
||||
{
|
||||
// The path we're given is from the guest so following other patterns we always ask the host information in its own form (aka convert on guest instead of on host).
|
||||
var ownerPath = _liveShareClientProvider.ConvertToSharedUri(textDocument.FilePath);
|
||||
|
||||
var hostProjectPath = _joinableTaskFactory.Run(() =>
|
||||
{
|
||||
var projectHierarchyProxy = _proxyAccessor.GetProjectHierarchyProxy();
|
||||
|
||||
// We need to block the foreground thread to get a proper project path. However, this is only done once on opening thedocument.
|
||||
return projectHierarchyProxy.GetProjectPathAsync(ownerPath, CancellationToken.None);
|
||||
});
|
||||
|
||||
return hostProjectPath;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Composition;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Host.Mef;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.VisualStudio.Editor.Razor;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Guest
|
||||
{
|
||||
[Shared]
|
||||
[ExportWorkspaceServiceFactory(typeof(ProjectPathProvider), Constants.GuestOnlyWorkspaceLayer)]
|
||||
internal class GuestProjectPathProviderFactory : IWorkspaceServiceFactory
|
||||
{
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
private readonly JoinableTaskContext _joinableTaskContext;
|
||||
private readonly ITextDocumentFactoryService _textDocumentFactory;
|
||||
private readonly LiveShareClientProvider _liveShareClientProvider;
|
||||
|
||||
[ImportingConstructor]
|
||||
public GuestProjectPathProviderFactory(
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
JoinableTaskContext joinableTaskContext,
|
||||
ITextDocumentFactoryService textDocumentFactory,
|
||||
LiveShareClientProvider liveShareClientProvider)
|
||||
{
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
if (joinableTaskContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(joinableTaskContext));
|
||||
}
|
||||
|
||||
if (textDocumentFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(textDocumentFactory));
|
||||
}
|
||||
|
||||
if (liveShareClientProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(liveShareClientProvider));
|
||||
}
|
||||
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
_joinableTaskContext = joinableTaskContext;
|
||||
_textDocumentFactory = textDocumentFactory;
|
||||
_liveShareClientProvider = liveShareClientProvider;
|
||||
}
|
||||
|
||||
public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
|
||||
{
|
||||
if (workspaceServices == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspaceServices));
|
||||
}
|
||||
|
||||
var languageServices = workspaceServices.GetLanguageServices(RazorLanguage.Name);
|
||||
var proxyAccessor = languageServices.GetRequiredService<ProxyAccessor>();
|
||||
|
||||
var projectPathProvider = new GuestProjectPathProvider(
|
||||
_foregroundDispatcher,
|
||||
_joinableTaskContext.Factory,
|
||||
_textDocumentFactory,
|
||||
proxyAccessor,
|
||||
_liveShareClientProvider);
|
||||
|
||||
return projectPathProvider;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Microsoft.CodeAnalysis.Razor.Workspaces.ProjectSystem;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Guest
|
||||
{
|
||||
internal class GuestProjectSnapshot : LiveShareProjectSnapshotBase
|
||||
{
|
||||
private readonly DefaultProjectSnapshot _innerProjectSnapshot;
|
||||
private readonly IReadOnlyList<TagHelperDescriptor> _tagHelpers;
|
||||
|
||||
public GuestProjectSnapshot(ProjectState projectState, IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||
{
|
||||
if (projectState == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectState));
|
||||
}
|
||||
|
||||
if (tagHelpers == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(tagHelpers));
|
||||
}
|
||||
|
||||
var snapshot = new DefaultProjectSnapshot(projectState);
|
||||
_innerProjectSnapshot = new DefaultProjectSnapshot(projectState);
|
||||
_tagHelpers = tagHelpers;
|
||||
}
|
||||
|
||||
public override RazorConfiguration Configuration => _innerProjectSnapshot.Configuration;
|
||||
|
||||
public override IEnumerable<string> DocumentFilePaths => _innerProjectSnapshot.DocumentFilePaths;
|
||||
|
||||
public override string FilePath => _innerProjectSnapshot.FilePath;
|
||||
|
||||
public override bool IsInitialized => true;
|
||||
|
||||
public override VersionStamp Version => _innerProjectSnapshot.Version;
|
||||
|
||||
public override Project WorkspaceProject => null;
|
||||
|
||||
public override DocumentSnapshot GetDocument(string filePath) => _innerProjectSnapshot.GetDocument(filePath);
|
||||
|
||||
public override RazorProjectEngine GetProjectEngine() => _innerProjectSnapshot.GetProjectEngine();
|
||||
|
||||
public override Task<IReadOnlyList<TagHelperDescriptor>> GetTagHelpersAsync() => Task.FromResult(_tagHelpers);
|
||||
|
||||
public override bool TryGetTagHelpers(out IReadOnlyList<TagHelperDescriptor> result)
|
||||
{
|
||||
result = _tagHelpers;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Guest
|
||||
{
|
||||
internal class GuestProjectSnapshotFactory : ProjectSnapshotFactory
|
||||
{
|
||||
private readonly Workspace _workspace;
|
||||
private readonly LiveShareClientProvider _liveShareClientProvider;
|
||||
|
||||
public GuestProjectSnapshotFactory(
|
||||
Workspace workspace,
|
||||
LiveShareClientProvider liveShareClientProvider)
|
||||
{
|
||||
if (workspace == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspace));
|
||||
}
|
||||
|
||||
if (liveShareClientProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(liveShareClientProvider));
|
||||
}
|
||||
|
||||
_workspace = workspace;
|
||||
_liveShareClientProvider = liveShareClientProvider;
|
||||
}
|
||||
|
||||
public override ProjectSnapshot Create(ProjectSnapshotHandleProxy projectHandle)
|
||||
{
|
||||
if (projectHandle == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectHandle));
|
||||
}
|
||||
|
||||
var filePath = _liveShareClientProvider.ConvertToLocalPath(projectHandle.FilePath);
|
||||
var hostProject = new HostProject(filePath, projectHandle.Configuration);
|
||||
var projectState = ProjectState.Create(_workspace.Services, hostProject);
|
||||
var snapshot = new GuestProjectSnapshot(projectState, projectHandle.TagHelpers);
|
||||
return snapshot;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Composition;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Host.Mef;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Guest
|
||||
{
|
||||
[Shared]
|
||||
[ExportLanguageServiceFactory(typeof(ProjectSnapshotFactory), RazorLanguage.Name, Constants.GuestOnlyWorkspaceLayer)]
|
||||
internal class GuestProjectSnapshotFactoryFactory : ILanguageServiceFactory
|
||||
{
|
||||
private readonly LiveShareClientProvider _liveShareClientProvider;
|
||||
|
||||
[ImportingConstructor]
|
||||
public GuestProjectSnapshotFactoryFactory(LiveShareClientProvider liveShareClientProvider)
|
||||
{
|
||||
if (liveShareClientProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(liveShareClientProvider));
|
||||
}
|
||||
|
||||
_liveShareClientProvider = liveShareClientProvider;
|
||||
}
|
||||
|
||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
||||
{
|
||||
if (languageServices == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(languageServices));
|
||||
}
|
||||
|
||||
var projectSnapshotFactory = new GuestProjectSnapshotFactory(languageServices.WorkspaceServices.Workspace, _liveShareClientProvider);
|
||||
return projectSnapshotFactory;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,204 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Guest
|
||||
{
|
||||
internal class GuestProjectSnapshotManager : ProjectSnapshotManager
|
||||
{
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
private readonly HostWorkspaceServices _services;
|
||||
private readonly ProjectSnapshotHandleStore _projectSnapshotHandleStore;
|
||||
private readonly ProjectSnapshotFactory _projectSnapshotFactory;
|
||||
private readonly LiveShareClientProvider _liveShareClientProvider;
|
||||
private readonly Workspace _workspace;
|
||||
private Dictionary<string, ProjectSnapshot> _projects;
|
||||
|
||||
public GuestProjectSnapshotManager(
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
HostWorkspaceServices services,
|
||||
ProjectSnapshotHandleStore projectSnapshotStore,
|
||||
ProjectSnapshotFactory projectSnapshotFactory,
|
||||
LiveShareClientProvider liveShareClientProvider,
|
||||
Workspace workspace)
|
||||
{
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
if (projectSnapshotStore == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectSnapshotStore));
|
||||
}
|
||||
|
||||
if (projectSnapshotFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectSnapshotFactory));
|
||||
}
|
||||
|
||||
if (liveShareClientProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(liveShareClientProvider));
|
||||
}
|
||||
|
||||
if (workspace == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspace));
|
||||
}
|
||||
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
_services = services;
|
||||
_projectSnapshotHandleStore = projectSnapshotStore;
|
||||
_projectSnapshotFactory = projectSnapshotFactory;
|
||||
_liveShareClientProvider = liveShareClientProvider;
|
||||
_workspace = workspace;
|
||||
_projectSnapshotHandleStore.Changed += ProjectSnapshotStore_Changed;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal GuestProjectSnapshotManager(
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
HostWorkspaceServices services,
|
||||
ProjectSnapshotHandleStore projectSnapshotHandleStore,
|
||||
ProjectSnapshotFactory projectSnapshotFactory,
|
||||
Workspace workspace)
|
||||
{
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
_services = services;
|
||||
_projectSnapshotHandleStore = projectSnapshotHandleStore;
|
||||
_projectSnapshotFactory = projectSnapshotFactory;
|
||||
_workspace = workspace;
|
||||
_projectSnapshotHandleStore.Changed += ProjectSnapshotStore_Changed;
|
||||
}
|
||||
|
||||
public override IReadOnlyList<ProjectSnapshot> Projects
|
||||
{
|
||||
get
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
EnsureProjects();
|
||||
|
||||
return _projects.Values.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public override event EventHandler<ProjectChangeEventArgs> Changed;
|
||||
|
||||
public override ProjectSnapshot GetLoadedProject(string filePath)
|
||||
{
|
||||
if (filePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(filePath));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
EnsureProjects();
|
||||
|
||||
if (_projects.TryGetValue(filePath, out var snapshot))
|
||||
{
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override ProjectSnapshot GetOrCreateProject(string filePath)
|
||||
{
|
||||
if (filePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(filePath));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
EnsureProjects();
|
||||
|
||||
return GetLoadedProject(filePath) ?? new EphemeralProjectSnapshot(_services, filePath);
|
||||
}
|
||||
|
||||
public override bool IsDocumentOpen(string documentFilePath)
|
||||
{
|
||||
if (documentFilePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documentFilePath));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
// On the guest side we don't currently support open document tracking.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal void ProjectSnapshotStore_Changed(object sender, ProjectProxyChangeEventArgs args)
|
||||
{
|
||||
if (args == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(args));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
var guestPath = ResolveGuestPath(args.ProjectFilePath);
|
||||
var oldProject = GetLoadedProject(guestPath);
|
||||
|
||||
// Reset projects cache so they'll be re-calculated.
|
||||
_projects = null;
|
||||
|
||||
var newProject = GetLoadedProject(guestPath);
|
||||
var changeArgs = new ProjectChangeEventArgs(oldProject, newProject, (ProjectChangeKind)args.Kind);
|
||||
|
||||
OnChanged(changeArgs);
|
||||
}
|
||||
|
||||
private void OnChanged(ProjectChangeEventArgs args)
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
Changed?.Invoke(this, args);
|
||||
}
|
||||
|
||||
private void EnsureProjects()
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
if (_projects == null)
|
||||
{
|
||||
UpdateProjects();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateProjects()
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
var projectHandles = _projectSnapshotHandleStore.GetProjectHandles();
|
||||
|
||||
_projects = projectHandles
|
||||
.Select(_projectSnapshotFactory.Create)
|
||||
.ToDictionary(project => project.FilePath, FilePathComparer.Instance);
|
||||
}
|
||||
|
||||
// Internal virtual for testing
|
||||
internal virtual string ResolveGuestPath(Uri filePath)
|
||||
{
|
||||
var guestPath = _liveShareClientProvider.ConvertToLocalPath(filePath);
|
||||
return guestPath;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Composition;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Host.Mef;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Guest
|
||||
{
|
||||
// This overrides the default Razor implementation of the ProjectSnapshotManager
|
||||
[ExportLanguageServiceFactory(typeof(ProjectSnapshotManager), RazorLanguage.Name, Constants.GuestOnlyWorkspaceLayer)]
|
||||
internal class GuestProjectSnapshotManagerFactory : ILanguageServiceFactory
|
||||
{
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
private readonly LiveShareClientProvider _liveShareClientProvider;
|
||||
|
||||
[ImportingConstructor]
|
||||
public GuestProjectSnapshotManagerFactory(
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
LiveShareClientProvider liveShareClientProvider)
|
||||
{
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
if (liveShareClientProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(liveShareClientProvider));
|
||||
}
|
||||
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
_liveShareClientProvider = liveShareClientProvider;
|
||||
}
|
||||
|
||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
||||
{
|
||||
if (languageServices == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(languageServices));
|
||||
}
|
||||
|
||||
var projectSnapshotStore = languageServices.GetRequiredService<ProjectSnapshotHandleStore>();
|
||||
var projectSnapshotFactory = languageServices.GetRequiredService<ProjectSnapshotFactory>();
|
||||
|
||||
var snapshotManager = new GuestProjectSnapshotManager(
|
||||
_foregroundDispatcher,
|
||||
languageServices.WorkspaceServices,
|
||||
projectSnapshotStore,
|
||||
projectSnapshotFactory,
|
||||
_liveShareClientProvider,
|
||||
languageServices.WorkspaceServices.Workspace);
|
||||
return snapshotManager;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.LiveShare.Razor.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Guest
|
||||
{
|
||||
[Export]
|
||||
[ExportCollaborationService(typeof(ICollaborationService), Scope = SessionScope.Guest)]
|
||||
public class LiveShareClientProvider : ICollaborationServiceFactory
|
||||
{
|
||||
private LiveShareClientService _liveShareClientService;
|
||||
|
||||
public Task<ICollaborationService> CreateServiceAsync(CollaborationSession session, CancellationToken cancellationToken)
|
||||
{
|
||||
var serializer = (JsonSerializer)session.GetService(typeof(JsonSerializer));
|
||||
serializer.Converters.RegisterRazorLiveShareConverters();
|
||||
|
||||
var liveShareClientService = new LiveShareClientService(session);
|
||||
liveShareClientService.Disposed += (s, e) =>
|
||||
{
|
||||
_liveShareClientService = null;
|
||||
};
|
||||
|
||||
_liveShareClientService = liveShareClientService;
|
||||
return Task.FromResult<ICollaborationService>(liveShareClientService);
|
||||
}
|
||||
|
||||
internal Task<TProxy> CreateServiceProxyAsync<TProxy>() where TProxy : class
|
||||
{
|
||||
if (_liveShareClientService == null)
|
||||
{
|
||||
return Task.FromResult<TProxy>(null);
|
||||
}
|
||||
|
||||
return _liveShareClientService.CreateServiceProxyAsync<TProxy>();
|
||||
}
|
||||
|
||||
internal string ConvertToLocalPath(Uri sharedUri)
|
||||
{
|
||||
return _liveShareClientService?.ConvertToLocalPath(sharedUri);
|
||||
}
|
||||
|
||||
internal Uri ConvertToSharedUri(string localPath)
|
||||
{
|
||||
return _liveShareClientService?.ConvertToSharedUri(localPath);
|
||||
}
|
||||
|
||||
private class LiveShareClientService : ICollaborationService, IDisposable
|
||||
{
|
||||
private CollaborationSession _session;
|
||||
|
||||
public LiveShareClientService(CollaborationSession session)
|
||||
{
|
||||
_session = session;
|
||||
}
|
||||
|
||||
public event EventHandler Disposed;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Disposed?.Invoke(this, null);
|
||||
}
|
||||
|
||||
internal Task<TProxy> CreateServiceProxyAsync<TProxy>() where TProxy : class
|
||||
{
|
||||
return _session.GetRemoteServiceAsync<TProxy>(typeof(TProxy).Name, CancellationToken.None);
|
||||
}
|
||||
|
||||
internal string ConvertToLocalPath(Uri sharedUri)
|
||||
{
|
||||
return _session.ConvertSharedUriToLocalPath(sharedUri);
|
||||
}
|
||||
|
||||
internal Uri ConvertToSharedUri(string localPath)
|
||||
{
|
||||
return _session.ConvertLocalPathToSharedUri(localPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Guest
|
||||
{
|
||||
internal abstract class ProjectSnapshotFactory : ILanguageService
|
||||
{
|
||||
public abstract ProjectSnapshot Create(ProjectSnapshotHandleProxy projectHandle);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Guest
|
||||
{
|
||||
internal abstract class ProjectSnapshotHandleStore : ILanguageService
|
||||
{
|
||||
public abstract IReadOnlyList<ProjectSnapshotHandleProxy> GetProjectHandles();
|
||||
|
||||
public abstract event EventHandler<ProjectProxyChangeEventArgs> Changed;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Guest
|
||||
{
|
||||
public abstract class ProxyAccessor : ILanguageService
|
||||
{
|
||||
public abstract IProjectSnapshotManagerProxy GetProjectSnapshotManagerProxy();
|
||||
|
||||
public abstract IProjectHierarchyProxy GetProjectHierarchyProxy();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
//// Copyright (c) .NET Foundation. All rights reserved.
|
||||
//// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
//using System;
|
||||
//using System.Collections.Generic;
|
||||
//using System.ComponentModel.Composition;
|
||||
//using System.Threading;
|
||||
//using System.Threading.Tasks;
|
||||
//using Microsoft.Cascade.Contracts;
|
||||
//using Microsoft.VisualStudio.Cascade.Contracts;
|
||||
|
||||
//namespace Microsoft.VisualStudio.LiveShare.Razor.Host
|
||||
//{
|
||||
// [ExportCollaborationService(typeof(ICollaborationService), Scope = SessionScope.Guest)]
|
||||
// internal class RazorGuestInitializationService : ICollaborationServiceFactory
|
||||
// {
|
||||
// private const string ViewImportsFileName = "_ViewImports.cshtml";
|
||||
// private readonly IVsRemoteWorkspaceManager _remoteWorkspaceManager;
|
||||
|
||||
// [ImportingConstructor]
|
||||
// public RazorGuestInitializationService(IVsRemoteWorkspaceManager remoteWorkspaceManager)
|
||||
// {
|
||||
// if (remoteWorkspaceManager == null)
|
||||
// {
|
||||
// throw new ArgumentNullException(nameof(remoteWorkspaceManager));
|
||||
// }
|
||||
|
||||
// _remoteWorkspaceManager = remoteWorkspaceManager;
|
||||
// }
|
||||
|
||||
// public async Task<ICollaborationService> CreateServiceAsync(CollaborationSession sessionContext, CancellationToken cancellationToken)
|
||||
// {
|
||||
// if (sessionContext == null)
|
||||
// {
|
||||
// throw new ArgumentNullException(nameof(sessionContext));
|
||||
// }
|
||||
|
||||
// await EnsureViewImportsCopiedAsync(cancellationToken);
|
||||
|
||||
// // Our services don't actually have any state that needs to be disposed.
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// // Today we ensure that all _ViewImports in the shared project exist on the guest because we don't currently track import documents
|
||||
// // in a manner that would allow us to retrieve/monitor that data across the wire. Once the Razor sub-system is moved to use
|
||||
// // DocumentSnapshots we'll be able to rely on that API to more properly manage files that impact parsing of Razor documents.
|
||||
// private async Task EnsureViewImportsCopiedAsync(CancellationToken cancellationToken)
|
||||
// {
|
||||
// var fileListOptions = new FileListOptions()
|
||||
// {
|
||||
// RecurseMode = FileRecurseMode.AllDescendants,
|
||||
// ExcludePatterns = new[]
|
||||
// {
|
||||
// "*.cs",
|
||||
// "*.js",
|
||||
// "*.css",
|
||||
// "*.html",
|
||||
// "*.json",
|
||||
// "*.csproj",
|
||||
// "*.sln",
|
||||
// "bin",
|
||||
// "obj",
|
||||
// }
|
||||
// };
|
||||
// var files = await _remoteWorkspaceManager.FileService.ListAsync(new[] { "/" }, fileListOptions, cancellationToken);
|
||||
|
||||
// var copyTasks = new List<Task>();
|
||||
// StartViewImportsCopy(files, copyTasks, cancellationToken);
|
||||
|
||||
// await Task.WhenAll(copyTasks);
|
||||
// }
|
||||
|
||||
// private void StartViewImportsCopy(FileInfo[] files, List<Task> copyTasks, CancellationToken cancellationToken)
|
||||
// {
|
||||
// foreach (var file in files)
|
||||
// {
|
||||
// if (file.IsDirectory)
|
||||
// {
|
||||
// StartViewImportsCopy(file.Children, copyTasks, cancellationToken);
|
||||
// }
|
||||
// else if (file.Path.EndsWith(ViewImportsFileName, StringComparison.OrdinalIgnoreCase))
|
||||
// {
|
||||
// // A _ViewImports.cshtml file, need to ensure it's copied to the guest.
|
||||
// var filePath = _remoteWorkspaceManager.ConvertToJoinerPath(file.Path, cancellationToken);
|
||||
// var copyTask = _remoteWorkspaceManager.WorkspaceFileService.EnsureFileExistsAsync(filePath, cancellationToken);
|
||||
// copyTasks.Add(copyTask);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.VisualStudio.Shell;
|
||||
using Microsoft.VisualStudio.Shell.Interop;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Host
|
||||
{
|
||||
internal class DefaultProjectHierarchyProxy : IProjectHierarchyProxy, ICollaborationService
|
||||
{
|
||||
private readonly CollaborationSession _session;
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
private readonly JoinableTaskFactory _joinableTaskFactory;
|
||||
private IVsUIShellOpenDocument _openDocumentShell;
|
||||
|
||||
public DefaultProjectHierarchyProxy(
|
||||
CollaborationSession session,
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
JoinableTaskFactory joinableTaskFactory)
|
||||
{
|
||||
if (session == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(session));
|
||||
}
|
||||
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
if (joinableTaskFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(joinableTaskFactory));
|
||||
}
|
||||
|
||||
_session = session;
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
_joinableTaskFactory = joinableTaskFactory;
|
||||
}
|
||||
|
||||
public async Task<Uri> GetProjectPathAsync(Uri documentFilePath, CancellationToken cancellationToken)
|
||||
{
|
||||
if (documentFilePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documentFilePath));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertBackgroundThread();
|
||||
|
||||
await _joinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
|
||||
|
||||
if (_openDocumentShell == null)
|
||||
{
|
||||
_openDocumentShell = ServiceProvider.GlobalProvider.GetService(typeof(SVsUIShellOpenDocument)) as IVsUIShellOpenDocument;
|
||||
}
|
||||
|
||||
var hostDocumentFilePath = _session.ConvertSharedUriToLocalPath(documentFilePath);
|
||||
var hr = _openDocumentShell.IsDocumentInAProject(hostDocumentFilePath, out var hierarchy, out _, out _, out _);
|
||||
if (ErrorHandler.Succeeded(hr) && hierarchy != null)
|
||||
{
|
||||
ErrorHandler.ThrowOnFailure(((IVsProject)hierarchy).GetMkDocument((uint)VSConstants.VSITEMID.Root, out var path), VSConstants.E_NOTIMPL);
|
||||
|
||||
return _session.ConvertLocalPathToSharedUri(path);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Host
|
||||
{
|
||||
[ExportCollaborationService(
|
||||
typeof(IProjectHierarchyProxy),
|
||||
Name = nameof(IProjectHierarchyProxy),
|
||||
Scope = SessionScope.Host,
|
||||
Role = ServiceRole.RemoteService)]
|
||||
internal class DefaultProjectHierarchyProxyFactory : ICollaborationServiceFactory
|
||||
{
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
private readonly JoinableTaskContext _joinableTaskContext;
|
||||
|
||||
[ImportingConstructor]
|
||||
public DefaultProjectHierarchyProxyFactory(
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
JoinableTaskContext joinableTaskContext)
|
||||
{
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
if (joinableTaskContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(joinableTaskContext));
|
||||
}
|
||||
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
_joinableTaskContext = joinableTaskContext;
|
||||
}
|
||||
|
||||
public Task<ICollaborationService> CreateServiceAsync(CollaborationSession session, CancellationToken cancellationToken)
|
||||
{
|
||||
if (session == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(session));
|
||||
}
|
||||
|
||||
var service = new DefaultProjectHierarchyProxy(session, _foregroundDispatcher, _joinableTaskContext.Factory);
|
||||
return Task.FromResult<ICollaborationService>(service);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Host
|
||||
{
|
||||
internal class DefaultProjectSnapshotManagerProxy : IProjectSnapshotManagerProxy, ICollaborationService, IDisposable
|
||||
{
|
||||
private readonly CollaborationSession _session;
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
private readonly ProjectSnapshotManager _projectSnapshotManager;
|
||||
private readonly JoinableTaskFactory _joinableTaskFactory;
|
||||
private readonly AsyncSemaphore _latestStateSemaphore;
|
||||
private bool _disposed;
|
||||
private ProjectSnapshotManagerProxyState _latestState;
|
||||
|
||||
// Internal for testing
|
||||
internal JoinableTask _processingChangedEventTestTask;
|
||||
|
||||
public DefaultProjectSnapshotManagerProxy(
|
||||
CollaborationSession session,
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
ProjectSnapshotManager projectSnapshotManager,
|
||||
JoinableTaskFactory joinableTaskFactory)
|
||||
{
|
||||
if (session == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(session));
|
||||
}
|
||||
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
if (projectSnapshotManager == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectSnapshotManager));
|
||||
}
|
||||
|
||||
if (joinableTaskFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(joinableTaskFactory));
|
||||
}
|
||||
|
||||
_session = session;
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
_projectSnapshotManager = projectSnapshotManager;
|
||||
_joinableTaskFactory = joinableTaskFactory;
|
||||
|
||||
_latestStateSemaphore = new AsyncSemaphore(initialCount: 1);
|
||||
_projectSnapshotManager.Changed += ProjectSnapshotManager_Changed;
|
||||
}
|
||||
|
||||
public event EventHandler<ProjectManagerProxyChangeEventArgs> Changed;
|
||||
|
||||
public async Task<ProjectSnapshotManagerProxyState> GetStateAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
using (await _latestStateSemaphore.EnterAsync().ConfigureAwait(false))
|
||||
{
|
||||
if (_latestState != null)
|
||||
{
|
||||
return _latestState;
|
||||
}
|
||||
}
|
||||
|
||||
var projects = await GetLatestProjectsAsync();
|
||||
var state = await CalculateUpdatedStateAsync(projects);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
_projectSnapshotManager.Changed -= ProjectSnapshotManager_Changed;
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal async Task<IReadOnlyList<ProjectSnapshot>> GetLatestProjectsAsync()
|
||||
{
|
||||
if (!_foregroundDispatcher.IsForegroundThread)
|
||||
{
|
||||
await _joinableTaskFactory.SwitchToMainThreadAsync(CancellationToken.None);
|
||||
}
|
||||
|
||||
return _projectSnapshotManager.Projects.ToArray();
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal async Task<ProjectSnapshotManagerProxyState> CalculateUpdatedStateAsync(IReadOnlyList<ProjectSnapshot> projects)
|
||||
{
|
||||
using (await _latestStateSemaphore.EnterAsync().ConfigureAwait(false))
|
||||
{
|
||||
var projectHandles = new List<ProjectSnapshotHandleProxy>();
|
||||
foreach (var project in projects)
|
||||
{
|
||||
var tagHelpers = await project.GetTagHelpersAsync().ConfigureAwait(false);
|
||||
var projectHandle = new ProjectSnapshotHandleProxy(_session.ConvertLocalPathToSharedUri(project.FilePath), tagHelpers, project.Configuration);
|
||||
projectHandles.Add(projectHandle);
|
||||
}
|
||||
|
||||
_latestState = new ProjectSnapshotManagerProxyState(projectHandles);
|
||||
return _latestState;
|
||||
}
|
||||
}
|
||||
|
||||
private void ProjectSnapshotManager_Changed(object sender, ProjectChangeEventArgs args)
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Kind == ProjectChangeKind.DocumentAdded ||
|
||||
args.Kind == ProjectChangeKind.DocumentRemoved ||
|
||||
args.Kind == ProjectChangeKind.DocumentChanged)
|
||||
{
|
||||
// Razor LiveShare doesn't currently support document based notifications over the wire.
|
||||
return;
|
||||
}
|
||||
|
||||
_processingChangedEventTestTask = _joinableTaskFactory.RunAsync(async () =>
|
||||
{
|
||||
var projects = await GetLatestProjectsAsync();
|
||||
var state = await CalculateUpdatedStateAsync(projects);
|
||||
|
||||
await _joinableTaskFactory.SwitchToMainThreadAsync();
|
||||
|
||||
var proxyArgs = new ProjectProxyChangeEventArgs(_session.ConvertLocalPathToSharedUri(args.ProjectFilePath), (ProjectProxyChangeKind)args.Kind);
|
||||
var remoteProjectChangeArgs = new ProjectManagerProxyChangeEventArgs(proxyArgs, state);
|
||||
|
||||
OnChanged(remoteProjectChangeArgs);
|
||||
});
|
||||
}
|
||||
|
||||
private void OnChanged(ProjectManagerProxyChangeEventArgs args)
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Changed?.Invoke(this, args);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Microsoft.VisualStudio.LanguageServices;
|
||||
using Microsoft.VisualStudio.LiveShare.Razor.Serialization;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Host
|
||||
{
|
||||
[ExportCollaborationService(
|
||||
typeof(IProjectSnapshotManagerProxy),
|
||||
Name = nameof(IProjectSnapshotManagerProxy),
|
||||
Scope = SessionScope.Host,
|
||||
Role = ServiceRole.RemoteService)]
|
||||
internal class DefaultProjectSnapshotManagerProxyFactory : ICollaborationServiceFactory
|
||||
{
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
private readonly JoinableTaskContext _joinableTaskContext;
|
||||
private readonly Workspace _workspace;
|
||||
|
||||
[ImportingConstructor]
|
||||
public DefaultProjectSnapshotManagerProxyFactory(
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
JoinableTaskContext joinableTaskContext,
|
||||
[Import(typeof(VisualStudioWorkspace))] Workspace workspace)
|
||||
{
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
if (joinableTaskContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(joinableTaskContext));
|
||||
}
|
||||
|
||||
if (workspace == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspace));
|
||||
}
|
||||
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
_joinableTaskContext = joinableTaskContext;
|
||||
|
||||
_workspace = workspace;
|
||||
}
|
||||
|
||||
public Task<ICollaborationService> CreateServiceAsync(CollaborationSession session, CancellationToken cancellationToken)
|
||||
{
|
||||
if (session == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(session));
|
||||
}
|
||||
|
||||
var serializer = (JsonSerializer)session.GetService(typeof(JsonSerializer));
|
||||
serializer.Converters.RegisterRazorLiveShareConverters();
|
||||
|
||||
var razorLanguageServices = _workspace.Services.GetLanguageServices(RazorLanguage.Name);
|
||||
var projectSnapshotManager = razorLanguageServices.GetRequiredService<ProjectSnapshotManager>();
|
||||
|
||||
var service = new DefaultProjectSnapshotManagerProxy(session, _foregroundDispatcher, projectSnapshotManager, _joinableTaskContext.Factory);
|
||||
return Task.FromResult<ICollaborationService>(service);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor
|
||||
{
|
||||
public interface IProjectHierarchyProxy
|
||||
{
|
||||
Task<Uri> GetProjectPathAsync(Uri documentFilePath, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor
|
||||
{
|
||||
public interface IProjectSnapshotManagerProxy
|
||||
{
|
||||
event EventHandler<ProjectManagerProxyChangeEventArgs> Changed;
|
||||
|
||||
Task<ProjectSnapshotManagerProxyState> GetStateAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net472</TargetFrameworks>
|
||||
<Description>Razor is a markup syntax for adding server-side logic to web pages. This package contains the Visual Studio Live Share Razor infrastructure.</Description>
|
||||
<EnableApiCheck>false</EnableApiCheck>
|
||||
<CodeAnalysisRuleSet>Microsoft.VisualStudio.LiveShare.Razor.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Microsoft.VisualStudio.LanguageServices.Razor\Serialization\*.cs">
|
||||
<Link>Serialization\%(FileName)%(Extension)</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.VisualStudio.Editor.Razor\Microsoft.VisualStudio.Editor.Razor.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.VisualStudio.LiveShare" Version="$(MicrosoftVisualStudioLiveSharePackageVersion)" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.LanguageServices" Version="$(VSIX_MicrosoftVisualStudioLanguageServicesPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Shell.15.0" Version="$(MicrosoftVisualStudioShell150PackageVersion)" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Threading" Version="$(MicrosoftVisualStudioThreadingPackageVersion)" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,93 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RuleSet Name="Microsoft Managed Recommended Rules" Description="These rules focus on the most critical problems in your code, including potential security holes, application crashes, and other important logic and design errors. It is recommended to include this rule set in any custom rule set you create for your projects." ToolsVersion="15.0">
|
||||
<Localization ResourceAssembly="Microsoft.VisualStudio.CodeAnalysis.RuleSets.Strings.dll" ResourceBaseName="Microsoft.VisualStudio.CodeAnalysis.RuleSets.Strings.Localized">
|
||||
<Name Resource="MinimumRecommendedRules_Name" />
|
||||
<Description Resource="MinimumRecommendedRules_Description" />
|
||||
</Localization>
|
||||
<Rules AnalyzerId="Microsoft.Analyzers.ManagedCodeAnalysis" RuleNamespace="Microsoft.Rules.Managed">
|
||||
<Rule Id="CA1001" Action="Warning" />
|
||||
<Rule Id="CA1009" Action="Warning" />
|
||||
<Rule Id="CA1016" Action="Warning" />
|
||||
<Rule Id="CA1033" Action="Warning" />
|
||||
<Rule Id="CA1049" Action="Warning" />
|
||||
<Rule Id="CA1060" Action="Warning" />
|
||||
<Rule Id="CA1061" Action="Warning" />
|
||||
<Rule Id="CA1063" Action="Warning" />
|
||||
<Rule Id="CA1065" Action="Warning" />
|
||||
<Rule Id="CA1301" Action="Warning" />
|
||||
<Rule Id="CA1400" Action="Warning" />
|
||||
<Rule Id="CA1401" Action="Warning" />
|
||||
<Rule Id="CA1403" Action="Warning" />
|
||||
<Rule Id="CA1404" Action="Warning" />
|
||||
<Rule Id="CA1405" Action="Warning" />
|
||||
<Rule Id="CA1410" Action="Warning" />
|
||||
<Rule Id="CA1415" Action="Warning" />
|
||||
<Rule Id="CA1821" Action="Warning" />
|
||||
<Rule Id="CA1900" Action="Warning" />
|
||||
<Rule Id="CA1901" Action="Warning" />
|
||||
<Rule Id="CA2002" Action="Warning" />
|
||||
<Rule Id="CA2100" Action="Warning" />
|
||||
<Rule Id="CA2101" Action="Warning" />
|
||||
<Rule Id="CA2108" Action="Warning" />
|
||||
<Rule Id="CA2111" Action="Warning" />
|
||||
<Rule Id="CA2112" Action="Warning" />
|
||||
<Rule Id="CA2114" Action="Warning" />
|
||||
<Rule Id="CA2116" Action="Warning" />
|
||||
<Rule Id="CA2117" Action="Warning" />
|
||||
<Rule Id="CA2122" Action="Warning" />
|
||||
<Rule Id="CA2123" Action="Warning" />
|
||||
<Rule Id="CA2124" Action="Warning" />
|
||||
<Rule Id="CA2126" Action="Warning" />
|
||||
<Rule Id="CA2131" Action="Warning" />
|
||||
<Rule Id="CA2132" Action="Warning" />
|
||||
<Rule Id="CA2133" Action="Warning" />
|
||||
<Rule Id="CA2134" Action="Warning" />
|
||||
<Rule Id="CA2137" Action="Warning" />
|
||||
<Rule Id="CA2138" Action="Warning" />
|
||||
<Rule Id="CA2140" Action="Warning" />
|
||||
<Rule Id="CA2141" Action="Warning" />
|
||||
<Rule Id="CA2146" Action="Warning" />
|
||||
<Rule Id="CA2147" Action="Warning" />
|
||||
<Rule Id="CA2149" Action="Warning" />
|
||||
<Rule Id="CA2200" Action="Warning" />
|
||||
<Rule Id="CA2202" Action="Warning" />
|
||||
<Rule Id="CA2207" Action="Warning" />
|
||||
<Rule Id="CA2212" Action="Warning" />
|
||||
<Rule Id="CA2213" Action="Warning" />
|
||||
<Rule Id="CA2214" Action="Warning" />
|
||||
<Rule Id="CA2216" Action="Warning" />
|
||||
<Rule Id="CA2220" Action="Warning" />
|
||||
<Rule Id="CA2229" Action="Warning" />
|
||||
<Rule Id="CA2231" Action="Warning" />
|
||||
<Rule Id="CA2232" Action="Warning" />
|
||||
<Rule Id="CA2235" Action="Warning" />
|
||||
<Rule Id="CA2236" Action="Warning" />
|
||||
<Rule Id="CA2237" Action="Warning" />
|
||||
<Rule Id="CA2238" Action="Warning" />
|
||||
<Rule Id="CA2240" Action="Warning" />
|
||||
<Rule Id="CA2241" Action="Warning" />
|
||||
<Rule Id="CA2242" Action="Warning" />
|
||||
</Rules>
|
||||
<Rules AnalyzerId="Microsoft.VisualStudio.Threading.Analyzers" RuleNamespace="Microsoft.VisualStudio.Threading.Analyzers">
|
||||
<Rule Id="VSTHRD001" Action="Info" />
|
||||
<Rule Id="VSTHRD002" Action="Info" />
|
||||
<Rule Id="VSTHRD003" Action="Info" />
|
||||
<Rule Id="VSTHRD004" Action="Info" />
|
||||
<Rule Id="VSTHRD010" Action="Info" />
|
||||
<Rule Id="VSTHRD011" Action="Info" />
|
||||
<Rule Id="VSTHRD012" Action="Info" />
|
||||
<Rule Id="VSTHRD100" Action="Info" />
|
||||
<Rule Id="VSTHRD101" Action="Info" />
|
||||
<Rule Id="VSTHRD102" Action="Info" />
|
||||
<Rule Id="VSTHRD103" Action="Info" />
|
||||
<Rule Id="VSTHRD104" Action="Info" />
|
||||
<Rule Id="VSTHRD105" Action="Info" />
|
||||
<Rule Id="VSTHRD106" Action="Info" />
|
||||
<Rule Id="VSTHRD107" Action="Info" />
|
||||
<Rule Id="VSTHRD108" Action="Info" />
|
||||
<Rule Id="VSTHRD109" Action="Info" />
|
||||
<Rule Id="VSTHRD110" Action="Info" />
|
||||
<Rule Id="VSTHRD111" Action="Info" />
|
||||
<Rule Id="VSTHRD200" Action="Info" />
|
||||
</Rules>
|
||||
</RuleSet>
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor
|
||||
{
|
||||
public sealed class ProjectManagerProxyChangeEventArgs : EventArgs
|
||||
{
|
||||
public ProjectManagerProxyChangeEventArgs(
|
||||
ProjectProxyChangeEventArgs change,
|
||||
ProjectSnapshotManagerProxyState state)
|
||||
{
|
||||
if (change == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(change));
|
||||
}
|
||||
|
||||
if (state == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(state));
|
||||
}
|
||||
|
||||
Change = change;
|
||||
State = state;
|
||||
}
|
||||
|
||||
public ProjectProxyChangeEventArgs Change { get; }
|
||||
|
||||
public ProjectSnapshotManagerProxyState State { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor
|
||||
{
|
||||
public sealed class ProjectProxyChangeEventArgs
|
||||
{
|
||||
public ProjectProxyChangeEventArgs(
|
||||
Uri projectFilePath,
|
||||
ProjectProxyChangeKind kind)
|
||||
{
|
||||
ProjectFilePath = projectFilePath;
|
||||
Kind = kind;
|
||||
}
|
||||
|
||||
public Uri ProjectFilePath { get; }
|
||||
|
||||
public ProjectProxyChangeKind Kind { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor
|
||||
{
|
||||
public enum ProjectProxyChangeKind
|
||||
{
|
||||
ProjectAdded,
|
||||
ProjectRemoved,
|
||||
ProjectChanged,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor
|
||||
{
|
||||
public sealed class ProjectSnapshotHandleProxy
|
||||
{
|
||||
public ProjectSnapshotHandleProxy(
|
||||
Uri filePath,
|
||||
IReadOnlyList<TagHelperDescriptor> tagHelpers,
|
||||
RazorConfiguration configuration)
|
||||
{
|
||||
if (filePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(filePath));
|
||||
}
|
||||
|
||||
if (tagHelpers == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(tagHelpers));
|
||||
}
|
||||
|
||||
FilePath = filePath;
|
||||
TagHelpers = tagHelpers;
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public RazorConfiguration Configuration { get; }
|
||||
|
||||
public IReadOnlyList<TagHelperDescriptor> TagHelpers { get; }
|
||||
|
||||
public Uri FilePath { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor
|
||||
{
|
||||
public sealed class ProjectSnapshotManagerProxyState
|
||||
{
|
||||
public ProjectSnapshotManagerProxyState(IReadOnlyList<ProjectSnapshotHandleProxy> projectHandles)
|
||||
{
|
||||
if (projectHandles == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectHandles));
|
||||
}
|
||||
|
||||
ProjectHandles = projectHandles;
|
||||
}
|
||||
|
||||
public IReadOnlyList<ProjectSnapshotHandleProxy> ProjectHandles { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.LiveShare.Razor.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Serialization
|
||||
{
|
||||
internal static class LiveShareJsonConverterCollectionExtensions
|
||||
{
|
||||
public static void RegisterRazorLiveShareConverters(this JsonConverterCollection collection)
|
||||
{
|
||||
if (collection == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(collection));
|
||||
}
|
||||
|
||||
if (collection.Contains(ProjectSnapshotHandleProxyJsonConverter.Instance))
|
||||
{
|
||||
// Already registered.
|
||||
return;
|
||||
}
|
||||
|
||||
collection.Add(ProjectSnapshotHandleProxyJsonConverter.Instance);
|
||||
collection.RegisterRazorConverters();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Serialization
|
||||
{
|
||||
internal class ProjectSnapshotHandleProxyJsonConverter : JsonConverter
|
||||
{
|
||||
public static readonly ProjectSnapshotHandleProxyJsonConverter Instance = new ProjectSnapshotHandleProxyJsonConverter();
|
||||
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return typeof(ProjectSnapshotHandleProxy).IsAssignableFrom(objectType);
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (reader.TokenType != JsonToken.StartObject)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var obj = JObject.Load(reader);
|
||||
var filePath = obj[nameof(ProjectSnapshotHandleProxy.FilePath)].ToObject<Uri>(serializer);
|
||||
var tagHelpers = obj[nameof(ProjectSnapshotHandleProxy.TagHelpers)].ToObject<List<TagHelperDescriptor>>(serializer);
|
||||
var configuration = obj[nameof(ProjectSnapshotHandleProxy.Configuration)].ToObject<RazorConfiguration>(serializer);
|
||||
|
||||
return new ProjectSnapshotHandleProxy(filePath, tagHelpers, configuration);
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
var handle = (ProjectSnapshotHandleProxy)value;
|
||||
|
||||
writer.WriteStartObject();
|
||||
|
||||
writer.WritePropertyName(nameof(ProjectSnapshotHandleProxy.FilePath));
|
||||
writer.WriteValue(handle.FilePath);
|
||||
|
||||
writer.WritePropertyName(nameof(ProjectSnapshotHandleProxy.TagHelpers));
|
||||
serializer.Serialize(writer, handle.TagHelpers);
|
||||
|
||||
if (handle.Configuration == null)
|
||||
{
|
||||
writer.WritePropertyName(nameof(ProjectSnapshotHandleProxy.Configuration));
|
||||
writer.WriteNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WritePropertyName(nameof(ProjectSnapshotHandleProxy.Configuration));
|
||||
serializer.Serialize(writer, handle.Configuration);
|
||||
}
|
||||
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -93,6 +93,7 @@
|
|||
<ProjectReference Include="..\Microsoft.CodeAnalysis.Remote.Razor\Microsoft.CodeAnalysis.Remote.Razor.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.VisualStudio.Editor.Razor\Microsoft.VisualStudio.Editor.Razor.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.VisualStudio.LanguageServices.Razor\Microsoft.VisualStudio.LanguageServices.Razor.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.VisualStudio.LiveShare.Razor\Microsoft.VisualStudio.LiveShare.Razor.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -38,5 +38,7 @@
|
|||
<Asset Type="Microsoft.VisualStudio.MefComponent" Path="Microsoft.VisualStudio.LanguageServices.Razor.dll" />
|
||||
<Asset Type="Microsoft.VisualStudio.Assembly" Path="Microsoft.VisualStudio.Editor.Razor.dll" />
|
||||
<Asset Type="Microsoft.VisualStudio.MefComponent" Path="Microsoft.VisualStudio.Editor.Razor.dll" />
|
||||
<Asset Type="Microsoft.VisualStudio.Assembly" Path="Microsoft.VisualStudio.LiveShare.Razor.dll" />
|
||||
<Asset Type="Microsoft.VisualStudio.MefComponent" Path="Microsoft.VisualStudio.LiveShare.Razor.dll" />
|
||||
</Assets>
|
||||
</PackageManifest>
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.LiveShare.Razor.Test;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Guest
|
||||
{
|
||||
public class DefaultProjectSnapshotHandleStoreTest : ForegroundDispatcherTestBase
|
||||
{
|
||||
public DefaultProjectSnapshotHandleStoreTest()
|
||||
{
|
||||
var joinableTaskContext = new JoinableTaskContextNode(new JoinableTaskContext());
|
||||
JoinableTaskFactory = new JoinableTaskFactory(joinableTaskContext.Context);
|
||||
}
|
||||
|
||||
public JoinableTaskFactory JoinableTaskFactory { get; }
|
||||
|
||||
[Fact]
|
||||
public void GetProjectHandles_ReturnsCurrentProjects()
|
||||
{
|
||||
// Arrange
|
||||
var snapshotStore = new DefaultProjectSnapshotHandleStore(Dispatcher, JoinableTaskFactory, Mock.Of<ProxyAccessor>());
|
||||
snapshotStore.UpdateProjects(
|
||||
new ProjectSnapshotManagerProxyState(new[]
|
||||
{
|
||||
TestProjectSnapshotHandleProxy.Create(new Uri("vsls:/some/other/project.csproj")),
|
||||
TestProjectSnapshotHandleProxy.Create(new Uri("vsls:/some/path/project.csproj")),
|
||||
}));
|
||||
|
||||
// Act
|
||||
var projects = snapshotStore.GetProjectHandles();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, projects.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetProjectHandles_ReturnsEmptyArrayIfNotInitialized()
|
||||
{
|
||||
// Arrange
|
||||
var snapshotStore = new DefaultProjectSnapshotHandleStore(Dispatcher, JoinableTaskFactory, Mock.Of<ProxyAccessor>());
|
||||
|
||||
// Act
|
||||
var projects = snapshotStore.GetProjectHandles();
|
||||
|
||||
// Assert
|
||||
Assert.Empty(projects);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoteProxyStateManager_UpdatesStateAndRaisesChangedEvent()
|
||||
{
|
||||
// Arrange
|
||||
var expectedChangeEventArgs = new ProjectProxyChangeEventArgs(new Uri("vsls:/some/path/project.csproj"), ProjectProxyChangeKind.ProjectChanged);
|
||||
var expectedProjectManagerState = new ProjectSnapshotManagerProxyState(new[] { TestProjectSnapshotHandleProxy.Create(new Uri("vsls:/some/path/project.csproj")) });
|
||||
var setupArgs = new ProjectManagerProxyChangeEventArgs(expectedChangeEventArgs, expectedProjectManagerState);
|
||||
var snapshotStore = new DefaultProjectSnapshotHandleStore(Dispatcher, JoinableTaskFactory, Mock.Of<ProxyAccessor>());
|
||||
var called = false;
|
||||
snapshotStore.Changed += (sender, args) =>
|
||||
{
|
||||
called = true;
|
||||
Assert.Same(expectedChangeEventArgs, args);
|
||||
};
|
||||
|
||||
// Act
|
||||
snapshotStore.HostProxyStateManager_Changed(null, setupArgs);
|
||||
|
||||
// Assert
|
||||
Assert.True(called);
|
||||
Assert.Single(snapshotStore.GetProjectHandles());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InitializeProjects_RaisesChangeEventForEachProjectIfGetProjectHandlesWasCalledBeforeInitialization()
|
||||
{
|
||||
// Arrange
|
||||
var proxy = new Mock<IProjectSnapshotManagerProxy>();
|
||||
proxy.Setup(p => p.GetStateAsync(CancellationToken.None))
|
||||
.Returns(Task.FromResult(new ProjectSnapshotManagerProxyState(new[]
|
||||
{
|
||||
TestProjectSnapshotHandleProxy.Create(new Uri("vsls:/some/path/project.csproj")),
|
||||
TestProjectSnapshotHandleProxy.Create(new Uri("vsls:/some/other/project.csproj")),
|
||||
})));
|
||||
var accessor = Mock.Of<ProxyAccessor>(a => a.GetProjectSnapshotManagerProxy() == proxy.Object);
|
||||
var snapshotStore = new DefaultProjectSnapshotHandleStore(Dispatcher, JoinableTaskFactory, accessor);
|
||||
snapshotStore.GetProjectHandles();
|
||||
var calledWith = new List<ProjectProxyChangeEventArgs>();
|
||||
snapshotStore.Changed += (sender, args) => calledWith.Add(args);
|
||||
|
||||
// Act
|
||||
snapshotStore.InitializeProjects();
|
||||
await snapshotStore.TestInitializationTask;
|
||||
|
||||
// Assert
|
||||
Assert.Collection(calledWith,
|
||||
args =>
|
||||
{
|
||||
Assert.Equal(ProjectProxyChangeKind.ProjectAdded, args.Kind);
|
||||
Assert.Equal("vsls:/some/path/project.csproj", args.ProjectFilePath.ToString());
|
||||
},
|
||||
args =>
|
||||
{
|
||||
Assert.Equal(ProjectProxyChangeKind.ProjectAdded, args.Kind);
|
||||
Assert.Equal("vsls:/some/other/project.csproj", args.ProjectFilePath.ToString());
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InitializeProjects_UpdatesProjectStateWithoutTriggeringChange()
|
||||
{
|
||||
// Arrange
|
||||
var proxy = new Mock<IProjectSnapshotManagerProxy>();
|
||||
proxy.Setup(p => p.GetStateAsync(CancellationToken.None))
|
||||
.Returns(Task.FromResult(new ProjectSnapshotManagerProxyState(new[] { TestProjectSnapshotHandleProxy.Create(new Uri("vsls:/some/path/project.csproj")) })));
|
||||
var accessor = Mock.Of<ProxyAccessor>(a => a.GetProjectSnapshotManagerProxy() == proxy.Object);
|
||||
var snapshotStore = new DefaultProjectSnapshotHandleStore(Dispatcher, JoinableTaskFactory, accessor);
|
||||
snapshotStore.Changed += (sender, args) => throw new InvalidOperationException("This should not be called.");
|
||||
|
||||
// Act
|
||||
snapshotStore.InitializeProjects();
|
||||
await snapshotStore.TestInitializationTask;
|
||||
|
||||
// Assert
|
||||
Assert.Single(snapshotStore.GetProjectHandles());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InitializeProjects_DoesNotUpdateProjectsIfInitialized()
|
||||
{
|
||||
// Arrange
|
||||
var proxy = new Mock<IProjectSnapshotManagerProxy>();
|
||||
proxy.Setup(p => p.GetStateAsync(CancellationToken.None))
|
||||
.Returns(Task.FromResult(new ProjectSnapshotManagerProxyState(Array.Empty<ProjectSnapshotHandleProxy>())));
|
||||
var accessor = Mock.Of<ProxyAccessor>(a => a.GetProjectSnapshotManagerProxy() == proxy.Object);
|
||||
var snapshotStore = new DefaultProjectSnapshotHandleStore(Dispatcher, JoinableTaskFactory, accessor);
|
||||
|
||||
// Initialize the initial project state to a project list. This is pretending to be an update event from the proxy
|
||||
// that has newer information than initialization.
|
||||
snapshotStore.UpdateProjects(new ProjectSnapshotManagerProxyState(new[] { TestProjectSnapshotHandleProxy.Create(new Uri("vsls:/some/path/project.csproj")) }));
|
||||
|
||||
// Act
|
||||
snapshotStore.InitializeProjects();
|
||||
await snapshotStore.TestInitializationTask;
|
||||
|
||||
// Assert
|
||||
Assert.Single(snapshotStore.GetProjectHandles());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UpdateProjects_UpdatesProjectCollection()
|
||||
{
|
||||
// Arrange
|
||||
var snapshotStore = new DefaultProjectSnapshotHandleStore(Dispatcher, JoinableTaskFactory, Mock.Of<ProxyAccessor>());
|
||||
var expectedProject = TestProjectSnapshotHandleProxy.Create(new Uri("vsls:/some/path/project.csproj"));
|
||||
var remoteProjectSnapshotManagerState = new ProjectSnapshotManagerProxyState(new List<ProjectSnapshotHandleProxy>() { expectedProject });
|
||||
|
||||
// Act
|
||||
snapshotStore.UpdateProjects(remoteProjectSnapshotManagerState);
|
||||
|
||||
// Assert
|
||||
var project = Assert.Single(snapshotStore.GetProjectHandles());
|
||||
Assert.Same(expectedProject, project);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Guest
|
||||
{
|
||||
public class DefaultProxyAccessorTest
|
||||
{
|
||||
[Fact]
|
||||
public void GetProjectSnapshotManagerProxy_Caches()
|
||||
{
|
||||
// Arrange
|
||||
var proxy = Mock.Of<IProjectSnapshotManagerProxy>();
|
||||
var proxyAccessor = new TestProxyAccessor<IProjectSnapshotManagerProxy>(proxy);
|
||||
|
||||
// Act
|
||||
var proxy1 = proxyAccessor.GetProjectSnapshotManagerProxy();
|
||||
var proxy2 = proxyAccessor.GetProjectSnapshotManagerProxy();
|
||||
|
||||
// Assert
|
||||
Assert.Same(proxy1, proxy2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetProjectHierarchyProxy_Caches()
|
||||
{
|
||||
// Arrange
|
||||
var proxy = Mock.Of<IProjectHierarchyProxy>();
|
||||
var proxyAccessor = new TestProxyAccessor<IProjectHierarchyProxy>(proxy);
|
||||
|
||||
// Act
|
||||
var proxy1 = proxyAccessor.GetProjectHierarchyProxy();
|
||||
var proxy2 = proxyAccessor.GetProjectHierarchyProxy();
|
||||
|
||||
// Assert
|
||||
Assert.Same(proxy1, proxy2);
|
||||
}
|
||||
|
||||
private class TestProxyAccessor<TTestProxy> : DefaultProxyAccessor where TTestProxy : class
|
||||
{
|
||||
private readonly TTestProxy _proxy;
|
||||
|
||||
public TestProxyAccessor(TTestProxy proxy)
|
||||
{
|
||||
_proxy = proxy;
|
||||
}
|
||||
|
||||
internal override TProxy CreateServiceProxy<TProxy>()
|
||||
{
|
||||
if (typeof(TProxy) == typeof(TTestProxy))
|
||||
{
|
||||
return _proxy as TProxy;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("The proxy accessor was called with unexpected arguments.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.VisualStudio.LiveShare.Razor.Test;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Guest
|
||||
{
|
||||
public class GuestProjectPathProviderTest : ForegroundDispatcherTestBase
|
||||
{
|
||||
public GuestProjectPathProviderTest()
|
||||
{
|
||||
var joinableTaskContext = new JoinableTaskContextNode(new JoinableTaskContext());
|
||||
JoinableTaskFactory = new JoinableTaskFactory(joinableTaskContext.Context);
|
||||
}
|
||||
|
||||
public JoinableTaskFactory JoinableTaskFactory { get; }
|
||||
|
||||
[Fact]
|
||||
public void TryGetProjectPath_NoTextDocument_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var textBuffer = Mock.Of<ITextBuffer>();
|
||||
var projectPathProvider = new GuestProjectPathProvider(
|
||||
Dispatcher,
|
||||
JoinableTaskFactory,
|
||||
Mock.Of<ITextDocumentFactoryService>(),
|
||||
Mock.Of<ProxyAccessor>(),
|
||||
Mock.Of<LiveShareClientProvider>());
|
||||
|
||||
// Act
|
||||
var result = projectPathProvider.TryGetProjectPath(textBuffer, out var filePath);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(filePath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetProjectPath_NullHostProjectPath_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var textBuffer = Mock.Of<ITextBuffer>();
|
||||
var textDocument = Mock.Of<ITextDocument>();
|
||||
var textDocumentFactory = Mock.Of<ITextDocumentFactoryService>(factory => factory.TryGetTextDocument(textBuffer, out textDocument) == true);
|
||||
var projectPathProvider = new TestGuestProjectPathProvider(
|
||||
null,
|
||||
Dispatcher,
|
||||
JoinableTaskFactory,
|
||||
textDocumentFactory,
|
||||
Mock.Of<ProxyAccessor>(),
|
||||
Mock.Of<LiveShareClientProvider>());
|
||||
|
||||
// Act
|
||||
var result = projectPathProvider.TryGetProjectPath(textBuffer, out var filePath);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(filePath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TryGetProjectPath_ValidHostProjectPath_ReturnsTrueWithGuestNormalizedPathAsync()
|
||||
{
|
||||
// Arrange
|
||||
var textBuffer = Mock.Of<ITextBuffer>();
|
||||
var textDocument = Mock.Of<ITextDocument>();
|
||||
var textDocumentFactory = Mock.Of<ITextDocumentFactoryService>(factory => factory.TryGetTextDocument(textBuffer, out textDocument) == true);
|
||||
var expectedProjectPath = "/guest/path/project.csproj";
|
||||
|
||||
var collabSession = new TestCollaborationSession(isHost: false);
|
||||
var liveShareClientProvider = new LiveShareClientProvider();
|
||||
await liveShareClientProvider.CreateServiceAsync(collabSession, CancellationToken.None);
|
||||
|
||||
var projectPathProvider = new TestGuestProjectPathProvider(
|
||||
new Uri("vsls:/path/project.csproj"),
|
||||
Dispatcher,
|
||||
JoinableTaskFactory,
|
||||
textDocumentFactory,
|
||||
Mock.Of<ProxyAccessor>(),
|
||||
liveShareClientProvider);
|
||||
|
||||
// Act
|
||||
var result = projectPathProvider.TryGetProjectPath(textBuffer, out var filePath);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
Assert.Equal(expectedProjectPath, filePath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetHostProjectPath_AsksProxyForProjectPathAsync()
|
||||
{
|
||||
// Arrange
|
||||
var expectedGuestFilePath = "/guest/path/index.cshtml";
|
||||
var expectedHostFilePath = new Uri("vsls:/path/index.cshtml");
|
||||
var expectedHostProjectPath = new Uri("vsls:/path/project.csproj");
|
||||
|
||||
var collabSession = new TestCollaborationSession(isHost: true);
|
||||
var liveShareClientProvider = new LiveShareClientProvider();
|
||||
await liveShareClientProvider.CreateServiceAsync(collabSession, CancellationToken.None);
|
||||
|
||||
var proxy = Mock.Of<IProjectHierarchyProxy>(p => p.GetProjectPathAsync(expectedHostFilePath, CancellationToken.None) == Task.FromResult(expectedHostProjectPath));
|
||||
var proxyAccessor = Mock.Of<ProxyAccessor>(accessor => accessor.GetProjectHierarchyProxy() == proxy);
|
||||
var textDocument = Mock.Of<ITextDocument>(document => document.FilePath == expectedGuestFilePath);
|
||||
var projectPathProvider = new GuestProjectPathProvider(
|
||||
Dispatcher,
|
||||
JoinableTaskFactory,
|
||||
Mock.Of<ITextDocumentFactoryService>(),
|
||||
proxyAccessor,
|
||||
liveShareClientProvider);
|
||||
|
||||
// Act
|
||||
var hostProjectPath = projectPathProvider.GetHostProjectPath(textDocument);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedHostProjectPath, hostProjectPath);
|
||||
}
|
||||
|
||||
private class TestGuestProjectPathProvider : GuestProjectPathProvider
|
||||
{
|
||||
private readonly Uri _hostProjectPath;
|
||||
|
||||
public TestGuestProjectPathProvider(
|
||||
Uri hostProjectPath,
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
JoinableTaskFactory joinableTaskFactory,
|
||||
ITextDocumentFactoryService textDocumentFactory,
|
||||
ProxyAccessor proxyAccessor,
|
||||
LiveShareClientProvider remoteWorkspaceManager)
|
||||
: base(foregroundDispatcher, joinableTaskFactory, textDocumentFactory, proxyAccessor, remoteWorkspaceManager)
|
||||
{
|
||||
_hostProjectPath = hostProjectPath;
|
||||
}
|
||||
|
||||
internal override Uri GetHostProjectPath(ITextDocument textDocument) => _hostProjectPath;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.VisualStudio.LiveShare.Razor.Test;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Guest
|
||||
{
|
||||
public class GuestProjectSnapshotFactoryTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task Create_ConvertsFromHandleToSnapshotAsync()
|
||||
{
|
||||
// Arrange
|
||||
var configuration = RazorConfiguration.Create(RazorLanguageVersion.Version_1_1, "TestConfiguration", Enumerable.Empty<RazorExtension>());
|
||||
var expectedTagHelpers = new[] { TagHelperDescriptorBuilder.Create("test1", "TestAssembly1").Build() };
|
||||
var projectHandle = new ProjectSnapshotHandleProxy(new Uri("vsls:/path/project.csproj"), expectedTagHelpers, configuration);
|
||||
var collabSession = new TestCollaborationSession(isHost: false);
|
||||
var liveShareClientProvider = new LiveShareClientProvider();
|
||||
await liveShareClientProvider.CreateServiceAsync(collabSession, CancellationToken.None);
|
||||
var workspace = TestWorkspace.Create();
|
||||
var factory = new GuestProjectSnapshotFactory(workspace, liveShareClientProvider);
|
||||
|
||||
// Act
|
||||
var project = factory.Create(projectHandle);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(project.DocumentFilePaths);
|
||||
Assert.Equal(configuration, project.Configuration);
|
||||
Assert.Equal("/guest/path/project.csproj", project.FilePath);
|
||||
Assert.True(project.TryGetTagHelpers(out var tagHelpers));
|
||||
Assert.Equal(expectedTagHelpers, tagHelpers);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Microsoft.VisualStudio.LiveShare.Razor.Test;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Guest
|
||||
{
|
||||
public class GuestProjectSnapshotManagerTest : ForegroundDispatcherTestBase
|
||||
{
|
||||
public Workspace Workspace { get; } = TestWorkspace.Create();
|
||||
|
||||
[Fact]
|
||||
public void Projects_ReturnsCurrentProjects()
|
||||
{
|
||||
// Arrange
|
||||
var snapshotStore = CreateSnapshotStore(
|
||||
TestProjectSnapshotHandleProxy.Create(new Uri("vsls:/some/other/project.csproj")),
|
||||
TestProjectSnapshotHandleProxy.Create(new Uri("vsls:/some/path/project.csproj")));
|
||||
var snapshotManager = CreateSnapshotManager(snapshotStore, Workspace);
|
||||
|
||||
// Act & Assert
|
||||
Assert.Equal(2, snapshotManager.Projects.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLoadedProject_ReturnsNullIfCanNotFindProject()
|
||||
{
|
||||
// Arrange
|
||||
var snapshotStore = CreateSnapshotStore(
|
||||
TestProjectSnapshotHandleProxy.Create(new Uri("vsls:/some/other/project.csproj")),
|
||||
TestProjectSnapshotHandleProxy.Create(new Uri("vsls:/some/path/project.csproj")));
|
||||
var snapshotManager = CreateSnapshotManager(snapshotStore, Workspace);
|
||||
|
||||
// Act
|
||||
var project = snapshotManager.GetLoadedProject("/some/random/path/project.csproj");
|
||||
|
||||
// Assert
|
||||
Assert.Null(project);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLoadedProject_ReturnsExistingProject()
|
||||
{
|
||||
// Arrange
|
||||
var expectedProject = TestProjectSnapshotHandleProxy.Create(new Uri("vsls:/some/path/project.csproj"));
|
||||
var snapshotStore = CreateSnapshotStore(
|
||||
TestProjectSnapshotHandleProxy.Create(new Uri("vsls:/some/other/project.csproj")),
|
||||
expectedProject);
|
||||
var snapshotManager = CreateSnapshotManager(snapshotStore, Workspace);
|
||||
|
||||
// Act
|
||||
var project = snapshotManager.GetLoadedProject(expectedProject.FilePath.ToString());
|
||||
|
||||
// Assert
|
||||
Assert.IsType<TestProjectSnapshot>(project);
|
||||
Assert.Equal(expectedProject.FilePath.ToString(), project.FilePath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetOrCreateProject_CreatesNewProject()
|
||||
{
|
||||
// Arrange
|
||||
var snapshotStore = CreateSnapshotStore(TestProjectSnapshotHandleProxy.Create(new Uri("vsls:/some/path/project.csproj")));
|
||||
var workspace = TestWorkspace.Create();
|
||||
var snapshotManager = CreateSnapshotManager(snapshotStore, workspace);
|
||||
var expectedNewProjectPath = "/some/random/path/project.csproj";
|
||||
|
||||
// Act
|
||||
var project = snapshotManager.GetOrCreateProject(expectedNewProjectPath);
|
||||
|
||||
// Assert
|
||||
Assert.IsType<EphemeralProjectSnapshot>(project);
|
||||
Assert.Equal(expectedNewProjectPath, project.FilePath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetOrCreateProject_ReturnsExistingProject()
|
||||
{
|
||||
// Arrange
|
||||
var expectedProject = TestProjectSnapshotHandleProxy.Create(new Uri("vsls:/some/path/project.csproj"));
|
||||
var snapshotStore = CreateSnapshotStore(
|
||||
TestProjectSnapshotHandleProxy.Create(new Uri("vsls:/some/other/project.csproj")),
|
||||
expectedProject);
|
||||
var snapshotManager = CreateSnapshotManager(snapshotStore, Workspace);
|
||||
|
||||
// Act
|
||||
var project = snapshotManager.GetOrCreateProject(expectedProject.FilePath.ToString());
|
||||
|
||||
// Assert
|
||||
Assert.IsType<TestProjectSnapshot>(project);
|
||||
Assert.Equal(expectedProject.FilePath.ToString(), project.FilePath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectSnapshotStore_Changed_UpdatesStateAndRaisesChangedEvent()
|
||||
{
|
||||
// Arrange
|
||||
var expectedFilePath = new Uri("vsls:/some/path/project.csproj");
|
||||
var storeArgs = new ProjectProxyChangeEventArgs(expectedFilePath, ProjectProxyChangeKind.ProjectChanged);
|
||||
var snapshotStore = new TestSnapshotStore();
|
||||
snapshotStore.ProjectHandles.Add(TestProjectSnapshotHandleProxy.Create(new Uri("vsls:/some/path/project.csproj")));
|
||||
var snapshotManager = CreateSnapshotManager(snapshotStore, Workspace);
|
||||
var initialProjects = snapshotManager.Projects;
|
||||
snapshotStore.ProjectHandles.Clear();
|
||||
var called = false;
|
||||
snapshotManager.Changed += (sender, args) =>
|
||||
{
|
||||
called = true;
|
||||
Assert.Equal(expectedFilePath.ToString(), args.ProjectFilePath);
|
||||
Assert.Equal(ProjectChangeKind.ProjectChanged, args.Kind);
|
||||
};
|
||||
|
||||
// Act
|
||||
snapshotManager.ProjectSnapshotStore_Changed(null, storeArgs);
|
||||
|
||||
// Assert
|
||||
Assert.True(called);
|
||||
Assert.NotEqual(snapshotManager.Projects.Count, initialProjects.Count);
|
||||
}
|
||||
|
||||
private static ProjectSnapshotHandleStore CreateSnapshotStore(params ProjectSnapshotHandleProxy[] snapshotProxies)
|
||||
{
|
||||
var snapshotStore = new TestSnapshotStore();
|
||||
snapshotStore.ProjectHandles.AddRange(snapshotProxies);
|
||||
return snapshotStore;
|
||||
}
|
||||
|
||||
private GuestProjectSnapshotManager CreateSnapshotManager(ProjectSnapshotHandleStore projectSnapshotHandleStore, Workspace workspace)
|
||||
{
|
||||
return new TestProjectSnapshotManager(Dispatcher, workspace.Services, projectSnapshotHandleStore, workspace);
|
||||
}
|
||||
|
||||
private class TestSnapshotStore : ProjectSnapshotHandleStore
|
||||
{
|
||||
public override event EventHandler<ProjectProxyChangeEventArgs> Changed
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
public List<ProjectSnapshotHandleProxy> ProjectHandles { get; } = new List<ProjectSnapshotHandleProxy>();
|
||||
|
||||
public override IReadOnlyList<ProjectSnapshotHandleProxy> GetProjectHandles() => ProjectHandles;
|
||||
}
|
||||
|
||||
private class TestProjectSnapshotManager : GuestProjectSnapshotManager
|
||||
{
|
||||
internal TestProjectSnapshotManager(ForegroundDispatcher foregroundDispatcher, HostWorkspaceServices services, ProjectSnapshotHandleStore projectSnapshotHandleStore, Workspace workspace)
|
||||
: base(foregroundDispatcher, services, projectSnapshotHandleStore, new TestProjectSnapshotFactory(), workspace)
|
||||
{
|
||||
}
|
||||
|
||||
internal override string ResolveGuestPath(Uri filePath)
|
||||
{
|
||||
return filePath.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
private class TestProjectSnapshotFactory : ProjectSnapshotFactory
|
||||
{
|
||||
public override ProjectSnapshot Create(ProjectSnapshotHandleProxy projectHandle) => new TestProjectSnapshot(projectHandle.FilePath.ToString());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,225 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Microsoft.VisualStudio.LiveShare.Razor.Test;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Host
|
||||
{
|
||||
public class DefaultProjectSnapshotManagerProxyTest : ForegroundDispatcherTestBase
|
||||
{
|
||||
public DefaultProjectSnapshotManagerProxyTest()
|
||||
{
|
||||
var joinableTaskContext = new JoinableTaskContextNode(new JoinableTaskContext());
|
||||
JoinableTaskFactory = new JoinableTaskFactory(joinableTaskContext.Context);
|
||||
}
|
||||
|
||||
public JoinableTaskFactory JoinableTaskFactory { get; }
|
||||
|
||||
[Fact]
|
||||
public async Task CalculateUpdatedStateAsync_ReturnsStateForAllProjects()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelper1 = TagHelperDescriptorBuilder.Create("test1", "TestAssembly1").Build();
|
||||
var project1 = new TestProjectSnapshot("/host/path/to/project1.csproj", tagHelper1);
|
||||
var tagHelper2 = TagHelperDescriptorBuilder.Create("test2", "TestAssembly2").Build();
|
||||
var project2 = new TestProjectSnapshot("/host/path/to/project2.csproj", tagHelper2);
|
||||
var projects = new ProjectSnapshot[] { project1, project2 };
|
||||
var projectSnapshotManager = new TestProjectSnapshotManager(projects);
|
||||
var proxy = new DefaultProjectSnapshotManagerProxy(
|
||||
new TestCollaborationSession(true),
|
||||
Dispatcher,
|
||||
projectSnapshotManager,
|
||||
JoinableTaskFactory);
|
||||
|
||||
// Act
|
||||
var state = await JoinableTaskFactory.RunAsync(() => proxy.CalculateUpdatedStateAsync(projects));
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
state.ProjectHandles,
|
||||
handle =>
|
||||
{
|
||||
Assert.Equal("vsls:/path/to/project1.csproj", handle.FilePath.ToString());
|
||||
var tagHelper = Assert.Single(handle.TagHelpers);
|
||||
Assert.Equal(tagHelper1, tagHelper);
|
||||
},
|
||||
handle =>
|
||||
{
|
||||
Assert.Equal("vsls:/path/to/project2.csproj", handle.FilePath.ToString());
|
||||
var tagHelper = Assert.Single(handle.TagHelpers);
|
||||
Assert.Equal(tagHelper2, tagHelper);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Changed_TriggersOnSnapshotManagerChanged()
|
||||
{
|
||||
// Arrange
|
||||
var projectSnapshotManager = new TestProjectSnapshotManager(new TestProjectSnapshot("/host/path/to/project1.csproj"));
|
||||
var proxy = new DefaultProjectSnapshotManagerProxy(
|
||||
new TestCollaborationSession(true),
|
||||
Dispatcher,
|
||||
projectSnapshotManager,
|
||||
JoinableTaskFactory);
|
||||
var snapshot = new TestProjectSnapshot("/host/path/to/project1.csproj");
|
||||
var changedArgs = new ProjectChangeEventArgs(snapshot, snapshot, ProjectChangeKind.ProjectChanged);
|
||||
var called = false;
|
||||
proxy.Changed += (sender, args) =>
|
||||
{
|
||||
called = true;
|
||||
Assert.Equal($"vsls:/path/to/project1.csproj", args.Change.ProjectFilePath.ToString());
|
||||
Assert.Equal(ProjectProxyChangeKind.ProjectChanged, args.Change.Kind);
|
||||
var handle = Assert.Single(args.State.ProjectHandles);
|
||||
Assert.Equal("vsls:/path/to/project1.csproj", handle.FilePath.ToString());
|
||||
Assert.Empty(handle.TagHelpers);
|
||||
};
|
||||
|
||||
// Act
|
||||
projectSnapshotManager.TriggerChanged(changedArgs);
|
||||
await proxy._processingChangedEventTestTask.JoinAsync();
|
||||
|
||||
// Assert
|
||||
Assert.True(called);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Changed_NoopsIfProxyDisposed()
|
||||
{
|
||||
// Arrange
|
||||
var projectSnapshotManager = new TestProjectSnapshotManager(new TestProjectSnapshot("/host/path/to/project1.csproj"));
|
||||
var proxy = new DefaultProjectSnapshotManagerProxy(
|
||||
new TestCollaborationSession(true),
|
||||
Dispatcher,
|
||||
projectSnapshotManager,
|
||||
JoinableTaskFactory);
|
||||
var snapshot = new TestProjectSnapshot("/host/path/to/project1.csproj");
|
||||
var changedArgs = new ProjectChangeEventArgs(snapshot, snapshot, ProjectChangeKind.ProjectChanged);
|
||||
proxy.Changed += (sender, args) => throw new InvalidOperationException("Should not have been called.");
|
||||
proxy.Dispose();
|
||||
|
||||
// Act
|
||||
projectSnapshotManager.TriggerChanged(changedArgs);
|
||||
|
||||
// Assert
|
||||
Assert.Null(proxy._processingChangedEventTestTask);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetLatestProjectsAsync_ReturnsSnapshotManagerProjects()
|
||||
{
|
||||
// Arrange
|
||||
var expectedProject = new TestProjectSnapshot("/host/path/to/project1.csproj");
|
||||
var projectSnapshotManager = new TestProjectSnapshotManager(expectedProject);
|
||||
var proxy = new DefaultProjectSnapshotManagerProxy(
|
||||
new TestCollaborationSession(true),
|
||||
Dispatcher,
|
||||
projectSnapshotManager,
|
||||
JoinableTaskFactory);
|
||||
|
||||
// Act
|
||||
var projects = await proxy.GetLatestProjectsAsync();
|
||||
|
||||
// Assert
|
||||
var project = Assert.Single(projects);
|
||||
Assert.Same(expectedProject, project);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetStateAsync_ReturnsProjectState()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelper1 = TagHelperDescriptorBuilder.Create("test1", "TestAssembly1").Build();
|
||||
var project1 = new TestProjectSnapshot("/host/path/to/project1.csproj", tagHelper1);
|
||||
var tagHelper2 = TagHelperDescriptorBuilder.Create("test2", "TestAssembly2").Build();
|
||||
var project2 = new TestProjectSnapshot("/host/path/to/project2.csproj", tagHelper2);
|
||||
var projects = new ProjectSnapshot[] { project1, project2 };
|
||||
var projectSnapshotManager = new TestProjectSnapshotManager(projects);
|
||||
var proxy = new DefaultProjectSnapshotManagerProxy(
|
||||
new TestCollaborationSession(true),
|
||||
Dispatcher,
|
||||
projectSnapshotManager,
|
||||
JoinableTaskFactory);
|
||||
|
||||
// Act
|
||||
var state = await JoinableTaskFactory.RunAsync(() => proxy.GetStateAsync(CancellationToken.None));
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
state.ProjectHandles,
|
||||
handle =>
|
||||
{
|
||||
Assert.Equal("vsls:/path/to/project1.csproj", handle.FilePath.ToString());
|
||||
var tagHelper = Assert.Single(handle.TagHelpers);
|
||||
Assert.Equal(tagHelper1, tagHelper);
|
||||
},
|
||||
handle =>
|
||||
{
|
||||
Assert.Equal("vsls:/path/to/project2.csproj", handle.FilePath.ToString());
|
||||
var tagHelper = Assert.Single(handle.TagHelpers);
|
||||
Assert.Equal(tagHelper2, tagHelper);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetStateAsync_CachesState()
|
||||
{
|
||||
// Arrange
|
||||
var project = new TestProjectSnapshot("/host/path/to/project2.csproj");
|
||||
var projectSnapshotManager = new TestProjectSnapshotManager(project);
|
||||
var proxy = new DefaultProjectSnapshotManagerProxy(
|
||||
new TestCollaborationSession(true),
|
||||
Dispatcher,
|
||||
projectSnapshotManager,
|
||||
JoinableTaskFactory);
|
||||
|
||||
// Act
|
||||
var state1 = await JoinableTaskFactory.RunAsync(() => proxy.GetStateAsync(CancellationToken.None));
|
||||
var state2 = await JoinableTaskFactory.RunAsync(() => proxy.GetStateAsync(CancellationToken.None));
|
||||
|
||||
// Assert
|
||||
Assert.Same(state1, state2);
|
||||
}
|
||||
|
||||
private class TestProjectSnapshotManager : ProjectSnapshotManager
|
||||
{
|
||||
public TestProjectSnapshotManager(params ProjectSnapshot[] projects)
|
||||
{
|
||||
Projects = projects;
|
||||
}
|
||||
|
||||
public override IReadOnlyList<ProjectSnapshot> Projects { get; }
|
||||
|
||||
public override event EventHandler<ProjectChangeEventArgs> Changed;
|
||||
|
||||
public void TriggerChanged(ProjectChangeEventArgs args)
|
||||
{
|
||||
Changed?.Invoke(this, args);
|
||||
}
|
||||
|
||||
public override ProjectSnapshot GetLoadedProject(string filePath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override ProjectSnapshot GetOrCreateProject(string filePath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override bool IsDocumentOpen(string documentFilePath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<CodeAnalysisRuleSet>..\..\src\Microsoft.VisualStudio.LiveShare.Razor\Microsoft.VisualStudio.LiveShare.Razor.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.VisualStudio.LiveShare.Razor\Microsoft.VisualStudio.LiveShare.Razor.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.VisualStudio.Editor.Razor.Test.Common\Microsoft.VisualStudio.Editor.Razor.Test.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Moq" Version="$(MoqPackageVersion)" />
|
||||
<PackageReference Include="xunit.analyzers" Version="$(XunitAnalyzersPackageVersion)" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitRunnerVisualStudioPackageVersion)" />
|
||||
<PackageReference Include="xunit" Version="$(XunitPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.VisualStudio.LiveShare.Razor.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor
|
||||
{
|
||||
public class SerializationTest
|
||||
{
|
||||
[Fact]
|
||||
public void ProjectSnapshotHandleProxy_RoundTripsProperly()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelpers = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("TestTagHelper", "TestAssembly").Build(),
|
||||
TagHelperDescriptorBuilder.Create("TestTagHelper2", "TestAssembly2").Build(),
|
||||
};
|
||||
var expectedConfiguration = RazorConfiguration.Default;
|
||||
var handle = new ProjectSnapshotHandleProxy(new Uri("vsls://some/path/project.csproj"), tagHelpers, RazorConfiguration.Default);
|
||||
var converterCollection = new JsonConverterCollection();
|
||||
converterCollection.RegisterRazorLiveShareConverters();
|
||||
var converters = converterCollection.ToArray();
|
||||
var serializedHandle = JsonConvert.SerializeObject(handle, converters);
|
||||
|
||||
// Act
|
||||
var deserializedHandle = JsonConvert.DeserializeObject<ProjectSnapshotHandleProxy>(serializedHandle, converters);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("vsls://some/path/project.csproj", deserializedHandle.FilePath.ToString());
|
||||
Assert.Equal(tagHelpers, deserializedHandle.TagHelpers);
|
||||
Assert.Equal(expectedConfiguration.ConfigurationName, deserializedHandle.Configuration.ConfigurationName);
|
||||
Assert.Equal(expectedConfiguration.Extensions.Count, deserializedHandle.Configuration.Extensions.Count);
|
||||
Assert.Equal(expectedConfiguration.LanguageVersion, deserializedHandle.Configuration.LanguageVersion);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Test
|
||||
{
|
||||
class TestCollaborationSession : CollaborationSession
|
||||
{
|
||||
private readonly bool _isHost;
|
||||
|
||||
public TestCollaborationSession(bool isHost)
|
||||
{
|
||||
_isHost = isHost;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assumes that host paths are prefixed with /host and guest paths are prefixed with /guest.
|
||||
/// Converts such paths to vsls: uris
|
||||
/// </summary>
|
||||
public override Uri ConvertLocalPathToSharedUri(string localPath)
|
||||
{
|
||||
var path = localPath.Replace("/host", "").Replace("/guest", "");
|
||||
return new Uri($"vsls:{path}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assumes that host paths are prefixed with /host and guest paths are prefixed with /guest.
|
||||
/// Converts vsls: uris to such paths.
|
||||
/// </summary>
|
||||
public override string ConvertSharedUriToLocalPath(Uri uri)
|
||||
{
|
||||
var path = uri.ToString().Replace("vsls:", "");
|
||||
return _isHost ? $"/host{path}" : $"/guest{path}";
|
||||
}
|
||||
|
||||
public override string SessionId => throw new NotImplementedException();
|
||||
public override IReadOnlyCollection<Peer> Peers => throw new NotImplementedException();
|
||||
public override IReadOnlyCollection<string> RemoteServiceNames => throw new NotImplementedException();
|
||||
public override int PeerNumber => throw new NotImplementedException();
|
||||
public override PeerIdentity Identity => throw new NotImplementedException();
|
||||
public override PeerRole Role => throw new NotImplementedException();
|
||||
public override PeerAccess Access => throw new NotImplementedException();
|
||||
|
||||
public override Task<string> DownloadFileAsync(Uri uri, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override Task<TService> GetRemoteServiceAsync<TService>(string name, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override object GetService(Type serviceType)
|
||||
{
|
||||
if (serviceType.Name == "JsonSerializer")
|
||||
{
|
||||
return new JsonSerializer();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Test
|
||||
{
|
||||
internal class TestProjectSnapshot : ProjectSnapshot
|
||||
{
|
||||
private readonly TagHelperDescriptor[] _tagHelpers;
|
||||
|
||||
public TestProjectSnapshot(string filePath, params TagHelperDescriptor[] tagHelpers)
|
||||
{
|
||||
FilePath = filePath;
|
||||
_tagHelpers = tagHelpers;
|
||||
}
|
||||
|
||||
public override RazorConfiguration Configuration => RazorConfiguration.Default;
|
||||
|
||||
public override IEnumerable<string> DocumentFilePaths => throw new NotImplementedException();
|
||||
|
||||
public override string FilePath { get; }
|
||||
|
||||
public override bool IsInitialized => throw new NotImplementedException();
|
||||
|
||||
public override VersionStamp Version => throw new NotImplementedException();
|
||||
|
||||
public override Project WorkspaceProject => throw new NotImplementedException();
|
||||
|
||||
public override DocumentSnapshot GetDocument(string filePath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override RazorProjectEngine GetProjectEngine()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override IEnumerable<DocumentSnapshot> GetRelatedDocuments(DocumentSnapshot document)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override bool IsImportDocument(DocumentSnapshot document)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override Task<IReadOnlyList<TagHelperDescriptor>> GetTagHelpersAsync()
|
||||
{
|
||||
return Task.FromResult((IReadOnlyList<TagHelperDescriptor>)_tagHelpers);
|
||||
}
|
||||
|
||||
public override bool TryGetTagHelpers(out IReadOnlyList<TagHelperDescriptor> results)
|
||||
{
|
||||
results = _tagHelpers;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Microsoft.VisualStudio.LiveShare.Razor.Test
|
||||
{
|
||||
internal static class TestProjectSnapshotHandleProxy
|
||||
{
|
||||
public static ProjectSnapshotHandleProxy Create(Uri filePath) => new ProjectSnapshotHandleProxy(filePath, Array.Empty<TagHelperDescriptor>(), RazorConfiguration.Default);
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче