Add documents, engine, tag helpers to snapshot
The project snapshot now maintains a RazorProjectEngine as well as set of Tag Helpers that are known for that snapshot. Pivoted some more services to be snapshot-centric. Also added the ability to track .cshtml documents to the project system. For now most components just ignore document changes.
This commit is contained in:
parent
d8990cc2b8
commit
e2edc280c5
|
|
@ -4,10 +4,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public abstract class RazorConfiguration
|
||||
public abstract class RazorConfiguration : IEquatable<RazorConfiguration>
|
||||
{
|
||||
public static readonly RazorConfiguration Default = new DefaultRazorConfiguration(
|
||||
RazorLanguageVersion.Latest,
|
||||
|
|
@ -43,6 +44,58 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
|
||||
public abstract RazorLanguageVersion LanguageVersion { get; }
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return base.Equals(obj as RazorConfiguration);
|
||||
}
|
||||
|
||||
public virtual bool Equals(RazorConfiguration other)
|
||||
{
|
||||
if (object.ReferenceEquals(other, null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (LanguageVersion != other.LanguageVersion)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ConfigurationName != other.ConfigurationName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Extensions.Count != other.Extensions.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < Extensions.Count; i++)
|
||||
{
|
||||
if (Extensions[i].ExtensionName != other.Extensions[i].ExtensionName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hash = new HashCodeCombiner();
|
||||
hash.Add(LanguageVersion);
|
||||
hash.Add(ConfigurationName);
|
||||
|
||||
for (var i = 0; i < Extensions.Count; i++)
|
||||
{
|
||||
hash.Add(Extensions[i].ExtensionName);
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
private class DefaultRazorConfiguration : RazorConfiguration
|
||||
{
|
||||
public DefaultRazorConfiguration(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
// 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.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
internal class DefaultProjectSnapshotProjectEngineFactory : ProjectSnapshotProjectEngineFactory
|
||||
{
|
||||
private readonly static RazorConfiguration DefaultConfiguration = FallbackRazorConfiguration.MVC_2_1;
|
||||
|
||||
private readonly IFallbackProjectEngineFactory _fallback;
|
||||
private readonly Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[] _factories;
|
||||
|
||||
public DefaultProjectSnapshotProjectEngineFactory(
|
||||
IFallbackProjectEngineFactory fallback,
|
||||
Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[] factories)
|
||||
{
|
||||
if (fallback == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(fallback));
|
||||
}
|
||||
|
||||
if (factories == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(factories));
|
||||
}
|
||||
|
||||
_fallback = fallback;
|
||||
_factories = factories;
|
||||
}
|
||||
|
||||
public override RazorProjectEngine Create(ProjectSnapshot project, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
if (fileSystem == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(fileSystem));
|
||||
}
|
||||
|
||||
// When we're running in the editor, the editor provides a configure delegate that will include
|
||||
// the editor settings and tag helpers.
|
||||
//
|
||||
// This service is only used in process in Visual Studio, and any other callers should provide these
|
||||
// things also.
|
||||
configure = configure ?? ((b) => { });
|
||||
|
||||
// The default configuration currently matches the newest MVC configuration.
|
||||
//
|
||||
// We typically want this because the language adds features over time - we don't want to a bunch of errors
|
||||
// to show up when a document is first opened, and then go away when the configuration loads, we'd prefer the opposite.
|
||||
var configuration = project.Configuration ?? DefaultConfiguration;
|
||||
|
||||
// If there's no factory to handle the configuration then fall back to a very basic configuration.
|
||||
//
|
||||
// This will stop a crash from happening in this case (misconfigured project), but will still make
|
||||
// it obvious to the user that something is wrong.
|
||||
var factory = SelectFactory(configuration) ?? _fallback;
|
||||
return factory.Create(configuration, fileSystem, configure);
|
||||
}
|
||||
|
||||
public override IProjectEngineFactory FindFactory(ProjectSnapshot project)
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
return SelectFactory(project.Configuration ?? DefaultConfiguration, requireSerializable: false);
|
||||
}
|
||||
|
||||
public override IProjectEngineFactory FindSerializableFactory(ProjectSnapshot project)
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
return SelectFactory(project.Configuration ?? DefaultConfiguration, requireSerializable: true);
|
||||
}
|
||||
|
||||
private IProjectEngineFactory SelectFactory(RazorConfiguration configuration, bool requireSerializable = false)
|
||||
{
|
||||
for (var i = 0; i < _factories.Length; i++)
|
||||
{
|
||||
var factory = _factories[i];
|
||||
if (string.Equals(configuration.ConfigurationName, factory.Metadata.ConfigurationName))
|
||||
{
|
||||
return requireSerializable && !factory.Metadata.SupportsSerialization ? null : factory.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.Workspaces
|
||||
{
|
||||
[ExportWorkspaceServiceFactory(typeof(ProjectSnapshotProjectEngineFactory))]
|
||||
internal class DefaultProjectSnapshotProjectEngineFactoryFactory : IWorkspaceServiceFactory
|
||||
{
|
||||
private readonly IFallbackProjectEngineFactory _fallback;
|
||||
private readonly Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[] _factories;
|
||||
|
||||
[ImportingConstructor]
|
||||
public DefaultProjectSnapshotProjectEngineFactoryFactory(
|
||||
IFallbackProjectEngineFactory fallback,
|
||||
[ImportMany] Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[] factories)
|
||||
{
|
||||
if (fallback == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(fallback));
|
||||
}
|
||||
|
||||
if (factories == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(factories));
|
||||
}
|
||||
|
||||
_fallback = fallback;
|
||||
_factories = factories;
|
||||
}
|
||||
|
||||
public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
|
||||
{
|
||||
if (workspaceServices == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspaceServices));
|
||||
}
|
||||
|
||||
return new DefaultProjectSnapshotProjectEngineFactory(_fallback, _factories);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,278 @@
|
|||
// 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.Composition;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
// Deliberately not exported for now, until this feature is working end to end.
|
||||
internal class BackgroundDocumentGenerator : ProjectSnapshotChangeTrigger
|
||||
{
|
||||
private ForegroundDispatcher _foregroundDispatcher;
|
||||
private ProjectSnapshotManagerBase _projectManager;
|
||||
|
||||
private readonly Dictionary<Key, DocumentSnapshot> _files;
|
||||
private Timer _timer;
|
||||
|
||||
[ImportingConstructor]
|
||||
public BackgroundDocumentGenerator(ForegroundDispatcher foregroundDispatcher)
|
||||
{
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
|
||||
_files = new Dictionary<Key, DocumentSnapshot>();
|
||||
}
|
||||
|
||||
public bool HasPendingNotifications
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_files)
|
||||
{
|
||||
return _files.Count > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Used in unit tests to control the timer delay.
|
||||
public TimeSpan Delay { get; set; } = TimeSpan.FromSeconds(2);
|
||||
|
||||
public bool IsScheduledOrRunning => _timer != null;
|
||||
|
||||
// Used in unit tests to ensure we can control when background work starts.
|
||||
public ManualResetEventSlim BlockBackgroundWorkStart { get; set; }
|
||||
|
||||
// Used in unit tests to ensure we can know when background work finishes.
|
||||
public ManualResetEventSlim NotifyBackgroundWorkStarting { get; set; }
|
||||
|
||||
// Used in unit tests to ensure we can control when background work completes.
|
||||
public ManualResetEventSlim BlockBackgroundWorkCompleting { get; set; }
|
||||
|
||||
// Used in unit tests to ensure we can know when background work finishes.
|
||||
public ManualResetEventSlim NotifyBackgroundWorkCompleted { get; set; }
|
||||
|
||||
private void OnStartingBackgroundWork()
|
||||
{
|
||||
if (BlockBackgroundWorkStart != null)
|
||||
{
|
||||
BlockBackgroundWorkStart.Wait();
|
||||
BlockBackgroundWorkStart.Reset();
|
||||
}
|
||||
|
||||
if (NotifyBackgroundWorkStarting != null)
|
||||
{
|
||||
NotifyBackgroundWorkStarting.Set();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCompletingBackgroundWork()
|
||||
{
|
||||
if (BlockBackgroundWorkCompleting != null)
|
||||
{
|
||||
BlockBackgroundWorkCompleting.Wait();
|
||||
BlockBackgroundWorkCompleting.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCompletedBackgroundWork()
|
||||
{
|
||||
if (NotifyBackgroundWorkCompleted != null)
|
||||
{
|
||||
NotifyBackgroundWorkCompleted.Set();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Initialize(ProjectSnapshotManagerBase projectManager)
|
||||
{
|
||||
if (projectManager == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectManager));
|
||||
}
|
||||
|
||||
_projectManager = projectManager;
|
||||
_projectManager.Changed += ProjectManager_Changed;
|
||||
}
|
||||
|
||||
protected virtual Task ProcessDocument(DocumentSnapshot document)
|
||||
{
|
||||
return document.GetGeneratedOutputAsync();
|
||||
}
|
||||
|
||||
public void Enqueue(ProjectSnapshot project, DocumentSnapshot document)
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
if (document == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(document));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
lock (_files)
|
||||
{
|
||||
// We only want to store the last 'seen' version of any given document. That way when we pick one to process
|
||||
// it's always the best version to use.
|
||||
_files.Add(new Key(project.FilePath, document.FilePath), document);
|
||||
|
||||
StartWorker();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void StartWorker()
|
||||
{
|
||||
// Access to the timer is protected by the lock in Enqueue and in Timer_Tick
|
||||
if (_timer == null)
|
||||
{
|
||||
// Timer will fire after a fixed delay, but only once.
|
||||
_timer = new Timer(Timer_Tick, null, Delay, Timeout.InfiniteTimeSpan);
|
||||
}
|
||||
}
|
||||
|
||||
private async void Timer_Tick(object state) // Yeah I know.
|
||||
{
|
||||
try
|
||||
{
|
||||
_foregroundDispatcher.AssertBackgroundThread();
|
||||
|
||||
// Timer is stopped.
|
||||
_timer.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
|
||||
OnStartingBackgroundWork();
|
||||
|
||||
DocumentSnapshot[] work;
|
||||
lock (_files)
|
||||
{
|
||||
work = _files.Values.ToArray();
|
||||
_files.Clear();
|
||||
}
|
||||
|
||||
for (var i = 0; i < work.Length; i++)
|
||||
{
|
||||
var document = work[i];
|
||||
try
|
||||
{
|
||||
await ProcessDocument(document);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ReportError(document, ex);
|
||||
}
|
||||
}
|
||||
|
||||
OnCompletingBackgroundWork();
|
||||
|
||||
lock (_files)
|
||||
{
|
||||
// Resetting the timer allows another batch of work to start.
|
||||
_timer.Dispose();
|
||||
_timer = null;
|
||||
|
||||
// If more work came in while we were running start the worker again.
|
||||
if (_files.Count > 0)
|
||||
{
|
||||
StartWorker();
|
||||
}
|
||||
}
|
||||
|
||||
OnCompletedBackgroundWork();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// This is something totally unexpected, let's just send it over to the workspace.
|
||||
await Task.Factory.StartNew(
|
||||
() => _projectManager.ReportError(ex),
|
||||
CancellationToken.None,
|
||||
TaskCreationOptions.None,
|
||||
_foregroundDispatcher.ForegroundScheduler);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReportError(DocumentSnapshot document, Exception ex)
|
||||
{
|
||||
GC.KeepAlive(Task.Factory.StartNew(
|
||||
() => _projectManager.ReportError(ex),
|
||||
CancellationToken.None,
|
||||
TaskCreationOptions.None,
|
||||
_foregroundDispatcher.ForegroundScheduler));
|
||||
}
|
||||
|
||||
private void ProjectManager_Changed(object sender, ProjectChangeEventArgs e)
|
||||
{
|
||||
switch (e.Kind)
|
||||
{
|
||||
case ProjectChangeKind.ProjectAdded:
|
||||
case ProjectChangeKind.ProjectChanged:
|
||||
case ProjectChangeKind.DocumentsChanged:
|
||||
{
|
||||
var project = _projectManager.GetLoadedProject(e.ProjectFilePath);
|
||||
foreach (var documentFilePath in project.DocumentFilePaths)
|
||||
{
|
||||
Enqueue(project, project.GetDocument(documentFilePath));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ProjectChangeKind.DocumentContentChanged:
|
||||
{
|
||||
throw null;
|
||||
}
|
||||
|
||||
case ProjectChangeKind.ProjectRemoved:
|
||||
// ignore
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException($"Unknown ProjectChangeKind {e.Kind}");
|
||||
}
|
||||
}
|
||||
|
||||
private struct Key : IEquatable<Key>
|
||||
{
|
||||
public Key(string projectFilePath, string documentFilePath)
|
||||
{
|
||||
ProjectFilePath = projectFilePath;
|
||||
DocumentFilePath = documentFilePath;
|
||||
}
|
||||
|
||||
public string ProjectFilePath { get; }
|
||||
|
||||
public string DocumentFilePath { get; }
|
||||
|
||||
public bool Equals(Key other)
|
||||
{
|
||||
return
|
||||
FilePathComparer.Instance.Equals(ProjectFilePath, other.ProjectFilePath) &&
|
||||
FilePathComparer.Instance.Equals(DocumentFilePath, other.DocumentFilePath);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is Key key ? Equals(key) : false;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hash = new HashCodeCombiner();
|
||||
hash.Add(ProjectFilePath, FilePathComparer.Instance);
|
||||
hash.Add(DocumentFilePath, FilePathComparer.Instance);
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
// 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.IO;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
internal abstract class ProjectSnapshotProjectEngineFactory : IWorkspaceService
|
||||
{
|
||||
public abstract IProjectEngineFactory FindFactory(ProjectSnapshot project);
|
||||
|
||||
public abstract IProjectEngineFactory FindSerializableFactory(ProjectSnapshot project);
|
||||
|
||||
public RazorProjectEngine Create(ProjectSnapshot project)
|
||||
{
|
||||
return Create(project, RazorProjectFileSystem.Create(Path.GetDirectoryName(project.FilePath)), null);
|
||||
}
|
||||
|
||||
public RazorProjectEngine Create(ProjectSnapshot project, RazorProjectFileSystem fileSystem)
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
if (fileSystem == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(fileSystem));
|
||||
}
|
||||
|
||||
return Create(project, fileSystem, null);
|
||||
}
|
||||
|
||||
public RazorProjectEngine Create(ProjectSnapshot project, Action<RazorProjectEngineBuilder> configure)
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
return Create(project, RazorProjectFileSystem.Create(Path.GetDirectoryName(project.FilePath)), configure);
|
||||
}
|
||||
|
||||
public abstract RazorProjectEngine Create(ProjectSnapshot project, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// 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.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal class DefaultDocumentSnapshot : DocumentSnapshot
|
||||
{
|
||||
public DefaultDocumentSnapshot(ProjectSnapshot project, DocumentState state)
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
if (state == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(state));
|
||||
}
|
||||
|
||||
Project = project;
|
||||
State = state;
|
||||
}
|
||||
|
||||
public ProjectSnapshot Project { get; }
|
||||
|
||||
public DocumentState State { get; }
|
||||
|
||||
public override string FilePath => State.HostDocument.FilePath;
|
||||
|
||||
public override string TargetPath => State.HostDocument.TargetPath;
|
||||
|
||||
public override Task<RazorCodeDocument> GetGeneratedOutputAsync()
|
||||
{
|
||||
// IMPORTANT: Don't put more code here. We want this to return a cached task.
|
||||
return State.GeneratedOutput.GetGeneratedOutputInitializationTask(Project, this);
|
||||
}
|
||||
|
||||
public override bool TryGetGeneratedOutput(out RazorCodeDocument results)
|
||||
{
|
||||
if (State.GeneratedOutput.IsResultAvailable)
|
||||
{
|
||||
results = State.GeneratedOutput.GetGeneratedOutputInitializationTask(Project, this).Result;
|
||||
return true;
|
||||
}
|
||||
|
||||
results = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,182 +3,82 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
// All of the public state of this is immutable - we create a new instance and notify subscribers
|
||||
// when it changes.
|
||||
//
|
||||
// However we use the private state to track things like dirty/clean.
|
||||
//
|
||||
// See the private constructors... When we update the snapshot we either are processing a Workspace
|
||||
// change (Project) or updating the computed state (ProjectSnapshotUpdateContext). We don't do both
|
||||
// at once.
|
||||
internal class DefaultProjectSnapshot : ProjectSnapshot
|
||||
{
|
||||
public DefaultProjectSnapshot(HostProject hostProject, Project workspaceProject, VersionStamp? version = null)
|
||||
private readonly object _lock;
|
||||
|
||||
private Dictionary<string, DefaultDocumentSnapshot> _documents;
|
||||
|
||||
public DefaultProjectSnapshot(ProjectState state)
|
||||
{
|
||||
if (hostProject == null)
|
||||
if (state == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hostProject));
|
||||
throw new ArgumentNullException(nameof(state));
|
||||
}
|
||||
|
||||
HostProject = hostProject;
|
||||
WorkspaceProject = workspaceProject; // Might be null
|
||||
|
||||
FilePath = hostProject.FilePath;
|
||||
Version = version ?? VersionStamp.Default;
|
||||
State = state;
|
||||
|
||||
_lock = new object();
|
||||
_documents = new Dictionary<string, DefaultDocumentSnapshot>(FilePathComparer.Instance);
|
||||
}
|
||||
|
||||
private DefaultProjectSnapshot(HostProject hostProject, DefaultProjectSnapshot other)
|
||||
{
|
||||
if (hostProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hostProject));
|
||||
}
|
||||
|
||||
if (other == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(other));
|
||||
}
|
||||
|
||||
ComputedVersion = other.ComputedVersion;
|
||||
|
||||
FilePath = other.FilePath;
|
||||
TagHelpers = other.TagHelpers;
|
||||
HostProject = hostProject;
|
||||
WorkspaceProject = other.WorkspaceProject;
|
||||
|
||||
Version = other.Version.GetNewerVersion();
|
||||
}
|
||||
|
||||
private DefaultProjectSnapshot(Project workspaceProject, DefaultProjectSnapshot other)
|
||||
{
|
||||
if (workspaceProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspaceProject));
|
||||
}
|
||||
|
||||
if (other == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(other));
|
||||
}
|
||||
|
||||
ComputedVersion = other.ComputedVersion;
|
||||
|
||||
FilePath = other.FilePath;
|
||||
TagHelpers = other.TagHelpers;
|
||||
HostProject = other.HostProject;
|
||||
WorkspaceProject = workspaceProject;
|
||||
|
||||
Version = other.Version.GetNewerVersion();
|
||||
}
|
||||
|
||||
private DefaultProjectSnapshot(ProjectSnapshotUpdateContext update, DefaultProjectSnapshot other)
|
||||
{
|
||||
if (update == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(update));
|
||||
}
|
||||
|
||||
if (other == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(other));
|
||||
}
|
||||
|
||||
ComputedVersion = update.Version;
|
||||
|
||||
FilePath = other.FilePath;
|
||||
HostProject = other.HostProject;
|
||||
TagHelpers = update.TagHelpers ?? Array.Empty<TagHelperDescriptor>();
|
||||
WorkspaceProject = other.WorkspaceProject;
|
||||
|
||||
// This doesn't represent a new version of the underlying data. Keep the same version.
|
||||
Version = other.Version;
|
||||
}
|
||||
public ProjectState State { get; }
|
||||
|
||||
public override RazorConfiguration Configuration => HostProject.Configuration;
|
||||
|
||||
public override string FilePath { get; }
|
||||
public override IEnumerable<string> DocumentFilePaths => State.Documents.Keys;
|
||||
|
||||
public override HostProject HostProject { get; }
|
||||
public override string FilePath => State.HostProject.FilePath;
|
||||
|
||||
public HostProject HostProject => State.HostProject;
|
||||
|
||||
public override bool IsInitialized => WorkspaceProject != null;
|
||||
|
||||
public override VersionStamp Version { get; }
|
||||
public override VersionStamp Version => State.Version;
|
||||
|
||||
public override Project WorkspaceProject { get; }
|
||||
public override Project WorkspaceProject => State.WorkspaceProject;
|
||||
|
||||
public override IReadOnlyList<TagHelperDescriptor> TagHelpers { get; } = Array.Empty<TagHelperDescriptor>();
|
||||
|
||||
// This is the version that the computed state is based on.
|
||||
public VersionStamp? ComputedVersion { get; set; }
|
||||
|
||||
// We know the project is dirty if we don't have a computed result, or it was computed for a different version.
|
||||
// Since the PSM updates the snapshots synchronously, the snapshot can never be older than the computed state.
|
||||
public bool IsDirty => ComputedVersion == null || ComputedVersion.Value != Version;
|
||||
|
||||
public ProjectSnapshotUpdateContext CreateUpdateContext()
|
||||
public override DocumentSnapshot GetDocument(string filePath)
|
||||
{
|
||||
return new ProjectSnapshotUpdateContext(FilePath, HostProject, WorkspaceProject, Version);
|
||||
lock (_lock)
|
||||
{
|
||||
if (!_documents.TryGetValue(filePath, out var result) &&
|
||||
State.Documents.TryGetValue(filePath, out var state))
|
||||
{
|
||||
result = new DefaultDocumentSnapshot(this, state);
|
||||
_documents.Add(filePath, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public DefaultProjectSnapshot WithHostProject(HostProject hostProject)
|
||||
public override RazorProjectEngine GetProjectEngine()
|
||||
{
|
||||
if (hostProject == null)
|
||||
return State.ProjectEngine.GetProjectEngine(this);
|
||||
}
|
||||
|
||||
public override Task<IReadOnlyList<TagHelperDescriptor>> GetTagHelpersAsync()
|
||||
{
|
||||
// IMPORTANT: Don't put more code here. We want this to return a cached task.
|
||||
return State.TagHelpers.GetTagHelperInitializationTask(this);
|
||||
}
|
||||
|
||||
public override bool TryGetTagHelpers(out IReadOnlyList<TagHelperDescriptor> results)
|
||||
{
|
||||
if (State.TagHelpers.IsResultAvailable)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hostProject));
|
||||
results = State.TagHelpers.GetTagHelperInitializationTask(this).Result;
|
||||
return true;
|
||||
}
|
||||
|
||||
return new DefaultProjectSnapshot(hostProject, this);
|
||||
}
|
||||
|
||||
public DefaultProjectSnapshot RemoveWorkspaceProject()
|
||||
{
|
||||
// We want to get rid of all of the computed state since it's not really valid.
|
||||
return new DefaultProjectSnapshot(HostProject, null, Version.GetNewerVersion());
|
||||
}
|
||||
|
||||
public DefaultProjectSnapshot WithWorkspaceProject(Project workspaceProject)
|
||||
{
|
||||
if (workspaceProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspaceProject));
|
||||
}
|
||||
|
||||
return new DefaultProjectSnapshot(workspaceProject, this);
|
||||
}
|
||||
|
||||
public DefaultProjectSnapshot WithComputedUpdate(ProjectSnapshotUpdateContext update)
|
||||
{
|
||||
if (update == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(update));
|
||||
}
|
||||
|
||||
return new DefaultProjectSnapshot(update, this);
|
||||
}
|
||||
|
||||
public bool HasConfigurationChanged(DefaultProjectSnapshot original)
|
||||
{
|
||||
if (original == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(original));
|
||||
}
|
||||
|
||||
return !object.Equals(Configuration, original.Configuration);
|
||||
}
|
||||
|
||||
public bool HaveTagHelpersChanged(ProjectSnapshot original)
|
||||
{
|
||||
if (original == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(original));
|
||||
}
|
||||
|
||||
return !Enumerable.SequenceEqual(TagHelpers, original.TagHelpers);
|
||||
results = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
|
|
@ -31,15 +30,14 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
private readonly ErrorReporter _errorReporter;
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
private readonly ProjectSnapshotChangeTrigger[] _triggers;
|
||||
private readonly ProjectSnapshotWorkerQueue _workerQueue;
|
||||
private readonly ProjectSnapshotWorker _worker;
|
||||
|
||||
private readonly Dictionary<string, DefaultProjectSnapshot> _projects;
|
||||
// Each entry holds a ProjectState and an optional ProjectSnapshot. ProjectSnapshots are
|
||||
// created lazily.
|
||||
private readonly Dictionary<string, Entry> _projects;
|
||||
|
||||
public DefaultProjectSnapshotManager(
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
ErrorReporter errorReporter,
|
||||
ProjectSnapshotWorker worker,
|
||||
IEnumerable<ProjectSnapshotChangeTrigger> triggers,
|
||||
Workspace workspace)
|
||||
{
|
||||
|
|
@ -53,11 +51,6 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
throw new ArgumentNullException(nameof(errorReporter));
|
||||
}
|
||||
|
||||
if (worker == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(worker));
|
||||
}
|
||||
|
||||
if (triggers == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(triggers));
|
||||
|
|
@ -70,13 +63,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
_errorReporter = errorReporter;
|
||||
_worker = worker;
|
||||
_triggers = triggers.ToArray();
|
||||
Workspace = workspace;
|
||||
|
||||
_projects = new Dictionary<string, DefaultProjectSnapshot>(FilePathComparer.Instance);
|
||||
|
||||
_workerQueue = new ProjectSnapshotWorkerQueue(_foregroundDispatcher, this, worker);
|
||||
_projects = new Dictionary<string, Entry>(FilePathComparer.Instance);
|
||||
|
||||
for (var i = 0; i < _triggers.Length; i++)
|
||||
{
|
||||
|
|
@ -89,43 +79,109 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
get
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
return _projects.Values.ToArray();
|
||||
|
||||
|
||||
var i = 0;
|
||||
var projects = new ProjectSnapshot[_projects.Count];
|
||||
foreach (var entry in _projects)
|
||||
{
|
||||
if (entry.Value.Snapshot == null)
|
||||
{
|
||||
entry.Value.Snapshot = new DefaultProjectSnapshot(entry.Value.State);
|
||||
}
|
||||
|
||||
projects[i++] = entry.Value.Snapshot;
|
||||
}
|
||||
|
||||
return projects;
|
||||
}
|
||||
}
|
||||
|
||||
public override Workspace Workspace { get; }
|
||||
|
||||
public override void ProjectUpdated(ProjectSnapshotUpdateContext update)
|
||||
public override ProjectSnapshot GetLoadedProject(string filePath)
|
||||
{
|
||||
if (update == null)
|
||||
if (filePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(update));
|
||||
throw new ArgumentNullException(nameof(filePath));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
if (_projects.TryGetValue(update.WorkspaceProject.FilePath, out var original))
|
||||
if (_projects.TryGetValue(filePath, out var entry))
|
||||
{
|
||||
if (!original.IsInitialized)
|
||||
if (entry.Snapshot == null)
|
||||
{
|
||||
// If the project has been uninitialized, just ignore the update.
|
||||
return;
|
||||
entry.Snapshot = new DefaultProjectSnapshot(entry.State);
|
||||
}
|
||||
|
||||
// This is an update to the project's computed values, so everything should be overwritten
|
||||
var snapshot = original.WithComputedUpdate(update);
|
||||
_projects[update.WorkspaceProject.FilePath] = snapshot;
|
||||
return entry.Snapshot;
|
||||
}
|
||||
|
||||
if (snapshot.IsDirty)
|
||||
return null;
|
||||
}
|
||||
|
||||
public override ProjectSnapshot GetOrCreateProject(string filePath)
|
||||
{
|
||||
if (filePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(filePath));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
return GetLoadedProject(filePath) ?? new EphemeralProjectSnapshot(Workspace.Services, filePath);
|
||||
}
|
||||
|
||||
public override void DocumentAdded(HostProject hostProject, HostDocument document)
|
||||
{
|
||||
if (hostProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hostProject));
|
||||
}
|
||||
|
||||
if (document == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(document));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
if (_projects.TryGetValue(hostProject.FilePath, out var entry))
|
||||
{
|
||||
var state = entry.State.AddHostDocument(document);
|
||||
|
||||
// Document updates can no-op.
|
||||
if (!object.ReferenceEquals(state, entry.State))
|
||||
{
|
||||
// It's possible that the snapshot can still be dirty if we got a project update while computing state in
|
||||
// the background. We need to trigger the background work to asynchronously compute the effect of the updates.
|
||||
NotifyBackgroundWorker(snapshot.CreateUpdateContext());
|
||||
_projects[hostProject.FilePath] = new Entry(state);
|
||||
NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, ProjectChangeKind.DocumentsChanged));
|
||||
}
|
||||
|
||||
if (!object.Equals(snapshot.ComputedVersion, original.ComputedVersion))
|
||||
}
|
||||
}
|
||||
|
||||
public override void DocumentRemoved(HostProject hostProject, HostDocument document)
|
||||
{
|
||||
if (hostProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hostProject));
|
||||
}
|
||||
|
||||
if (document == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(document));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
if (_projects.TryGetValue(hostProject.FilePath, out var entry))
|
||||
{
|
||||
var state = entry.State.RemoveHostDocument(document);
|
||||
|
||||
// Document updates can no-op.
|
||||
if (!object.ReferenceEquals(state, entry.State))
|
||||
{
|
||||
NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.TagHelpersChanged));
|
||||
_projects[hostProject.FilePath] = new Entry(state);
|
||||
NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, ProjectChangeKind.DocumentsChanged));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -149,17 +205,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// So if possible find a WorkspaceProject.
|
||||
var workspaceProject = GetWorkspaceProject(hostProject.FilePath);
|
||||
|
||||
var snapshot = new DefaultProjectSnapshot(hostProject, workspaceProject);
|
||||
_projects[hostProject.FilePath] = snapshot;
|
||||
|
||||
if (snapshot.IsInitialized && snapshot.IsDirty)
|
||||
{
|
||||
// Start computing background state if the project is fully initialized.
|
||||
NotifyBackgroundWorker(snapshot.CreateUpdateContext());
|
||||
}
|
||||
var state = new ProjectState(Workspace.Services, hostProject, workspaceProject);
|
||||
_projects[hostProject.FilePath] = new Entry(state);
|
||||
|
||||
// We need to notify listeners about every project add.
|
||||
NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.Added));
|
||||
NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, ProjectChangeKind.ProjectAdded));
|
||||
}
|
||||
|
||||
public override void HostProjectChanged(HostProject hostProject)
|
||||
|
|
@ -171,22 +221,17 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
if (_projects.TryGetValue(hostProject.FilePath, out var original))
|
||||
if (_projects.TryGetValue(hostProject.FilePath, out var entry))
|
||||
{
|
||||
// Doing an update to the project should keep computed values, but mark the project as dirty if the
|
||||
// underlying project is newer.
|
||||
var snapshot = original.WithHostProject(hostProject);
|
||||
_projects[hostProject.FilePath] = snapshot;
|
||||
var state = entry.State.WithHostProject(hostProject);
|
||||
|
||||
if (snapshot.IsInitialized && snapshot.IsDirty)
|
||||
// HostProject updates can no-op.
|
||||
if (!object.ReferenceEquals(state, entry.State))
|
||||
{
|
||||
// Start computing background state if the project is fully initialized.
|
||||
NotifyBackgroundWorker(snapshot.CreateUpdateContext());
|
||||
}
|
||||
_projects[hostProject.FilePath] = new Entry(state);
|
||||
|
||||
// Notify listeners right away because if the HostProject changes then it's likely that the Razor
|
||||
// configuration changed.
|
||||
NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.Changed));
|
||||
NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, ProjectChangeKind.ProjectChanged));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -204,37 +249,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
_projects.Remove(hostProject.FilePath);
|
||||
|
||||
// We need to notify listeners about every project removal.
|
||||
NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.Removed));
|
||||
}
|
||||
}
|
||||
|
||||
public override void HostProjectBuildComplete(HostProject hostProject)
|
||||
{
|
||||
if (hostProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hostProject));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
if (_projects.TryGetValue(hostProject.FilePath, out var original))
|
||||
{
|
||||
var workspaceProject = GetWorkspaceProject(hostProject.FilePath);
|
||||
if (workspaceProject == null)
|
||||
{
|
||||
// Host project was built prior to a workspace project being associated. We have nothing to do without
|
||||
// a workspace project so we short circuit.
|
||||
return;
|
||||
}
|
||||
|
||||
// Doing an update to the project should keep computed values, but mark the project as dirty if the
|
||||
// underlying project is newer.
|
||||
var snapshot = original.WithWorkspaceProject(workspaceProject);
|
||||
|
||||
_projects[hostProject.FilePath] = snapshot;
|
||||
|
||||
// Notify the background worker so it can trigger tag helper discovery.
|
||||
NotifyBackgroundWorker(snapshot.CreateUpdateContext());
|
||||
NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, ProjectChangeKind.ProjectRemoved));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -254,25 +269,16 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
// The WorkspaceProject initialization never triggers a "Project Add" from out point of view, we
|
||||
// only care if the new WorkspaceProject matches an existing HostProject.
|
||||
if (_projects.TryGetValue(workspaceProject.FilePath, out var original))
|
||||
if (_projects.TryGetValue(workspaceProject.FilePath, out var entry))
|
||||
{
|
||||
// If this is a multi-targeting project then we are only interested in a single workspace project. If we already
|
||||
// found one in the past just ignore this one.
|
||||
if (original.WorkspaceProject == null)
|
||||
if (entry.State.WorkspaceProject == null)
|
||||
{
|
||||
var snapshot = original.WithWorkspaceProject(workspaceProject);
|
||||
_projects[workspaceProject.FilePath] = snapshot;
|
||||
var state = entry.State.WithWorkspaceProject(workspaceProject);
|
||||
_projects[workspaceProject.FilePath] = new Entry(state);
|
||||
|
||||
if (snapshot.IsInitialized && snapshot.IsDirty)
|
||||
{
|
||||
// We don't need to notify listeners yet because we don't have any **new** computed state.
|
||||
//
|
||||
// However we do need to trigger the background work to asynchronously compute the effect of the updates.
|
||||
NotifyBackgroundWorker(snapshot.CreateUpdateContext());
|
||||
}
|
||||
|
||||
// Notify listeners right away since WorkspaceProject was just added, the project is now initialized.
|
||||
NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.Changed));
|
||||
NotifyListeners(new ProjectChangeEventArgs(workspaceProject.FilePath, ProjectChangeKind.ProjectChanged));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -293,25 +299,18 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
// We also need to check the projectId here. If this is a multi-targeting project then we are only interested
|
||||
// in a single workspace project. Just use the one that showed up first.
|
||||
if (_projects.TryGetValue(workspaceProject.FilePath, out var original) &&
|
||||
(original.WorkspaceProject == null ||
|
||||
original.WorkspaceProject.Id == workspaceProject.Id))
|
||||
if (_projects.TryGetValue(workspaceProject.FilePath, out var entry) &&
|
||||
(entry.State.WorkspaceProject == null || entry.State.WorkspaceProject.Id == workspaceProject.Id))
|
||||
{
|
||||
// Doing an update to the project should keep computed values, but mark the project as dirty if the
|
||||
// underlying project is newer.
|
||||
var snapshot = original.WithWorkspaceProject(workspaceProject);
|
||||
_projects[workspaceProject.FilePath] = snapshot;
|
||||
|
||||
if (snapshot.IsInitialized && snapshot.IsDirty)
|
||||
var state = entry.State.WithWorkspaceProject(workspaceProject);
|
||||
|
||||
// WorkspaceProject updates can no-op. This can be the case if a build is triggered, but we've
|
||||
// already seen the update.
|
||||
if (!object.ReferenceEquals(state, entry.State))
|
||||
{
|
||||
// We don't need to notify listeners yet because we don't have any **new** computed state. However we do
|
||||
// need to trigger the background work to asynchronously compute the effect of the updates.
|
||||
NotifyBackgroundWorker(snapshot.CreateUpdateContext());
|
||||
}
|
||||
_projects[workspaceProject.FilePath] = new Entry(state);
|
||||
|
||||
if (snapshot.HaveTagHelpersChanged(original))
|
||||
{
|
||||
NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.TagHelpersChanged));
|
||||
NotifyListeners(new ProjectChangeEventArgs(workspaceProject.FilePath, ProjectChangeKind.ProjectChanged));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -330,16 +329,16 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
return;
|
||||
}
|
||||
|
||||
if (_projects.TryGetValue(workspaceProject.FilePath, out var original))
|
||||
if (_projects.TryGetValue(workspaceProject.FilePath, out var entry))
|
||||
{
|
||||
// We also need to check the projectId here. If this is a multi-targeting project then we are only interested
|
||||
// in a single workspace project. Make sure the WorkspaceProject we're using is the one that's being removed.
|
||||
if (original.WorkspaceProject?.Id != workspaceProject.Id)
|
||||
if (entry.State.WorkspaceProject?.Id != workspaceProject.Id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DefaultProjectSnapshot snapshot;
|
||||
ProjectState state;
|
||||
|
||||
// So if the WorkspaceProject got removed, we should double check to make sure that there aren't others
|
||||
// hanging around. This could happen if a project is multi-targeting and one of the TFMs is removed.
|
||||
|
|
@ -347,30 +346,19 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
if (otherWorkspaceProject != null && otherWorkspaceProject.Id != workspaceProject.Id)
|
||||
{
|
||||
// OK there's another WorkspaceProject, use that.
|
||||
//
|
||||
// Doing an update to the project should keep computed values, but mark the project as dirty if the
|
||||
// underlying project is newer.
|
||||
snapshot = original.WithWorkspaceProject(otherWorkspaceProject);
|
||||
_projects[workspaceProject.FilePath] = snapshot;
|
||||
state = entry.State.WithWorkspaceProject(otherWorkspaceProject);
|
||||
_projects[otherWorkspaceProject.FilePath] = new Entry(state);
|
||||
|
||||
if (snapshot.IsInitialized && snapshot.IsDirty)
|
||||
{
|
||||
// We don't need to notify listeners yet because we don't have any **new** computed state. However we do
|
||||
// need to trigger the background work to asynchronously compute the effect of the updates.
|
||||
NotifyBackgroundWorker(snapshot.CreateUpdateContext());
|
||||
}
|
||||
|
||||
// Notify listeners of a change because it's a different WorkspaceProject.
|
||||
NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.Changed));
|
||||
|
||||
return;
|
||||
NotifyListeners(new ProjectChangeEventArgs(otherWorkspaceProject.FilePath, ProjectChangeKind.ProjectChanged));
|
||||
}
|
||||
else
|
||||
{
|
||||
state = entry.State.WithWorkspaceProject(null);
|
||||
_projects[workspaceProject.FilePath] = new Entry(state);
|
||||
|
||||
snapshot = original.RemoveWorkspaceProject();
|
||||
_projects[workspaceProject.FilePath] = snapshot;
|
||||
|
||||
// Notify listeners of a change because we've removed computed state.
|
||||
NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.Changed));
|
||||
// Notify listeners of a change because we've removed computed state.
|
||||
NotifyListeners(new ProjectChangeEventArgs(workspaceProject.FilePath, ProjectChangeKind.ProjectChanged));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -401,8 +389,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
var project = hostProject?.FilePath == null ? null : this.GetProjectWithFilePath(hostProject.FilePath);
|
||||
_errorReporter.ReportError(exception, project);
|
||||
var snapshot = hostProject?.FilePath == null ? null : GetLoadedProject(hostProject.FilePath);
|
||||
_errorReporter.ReportError(exception, snapshot);
|
||||
}
|
||||
|
||||
public override void ReportError(Exception exception, Project workspaceProject)
|
||||
|
|
@ -411,7 +399,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
|
||||
_errorReporter.ReportError(exception, workspaceProject);
|
||||
}
|
||||
|
||||
|
|
@ -440,14 +428,6 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
return null;
|
||||
}
|
||||
|
||||
// virtual so it can be overridden in tests
|
||||
protected virtual void NotifyBackgroundWorker(ProjectSnapshotUpdateContext context)
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
_workerQueue.Enqueue(context);
|
||||
}
|
||||
|
||||
// virtual so it can be overridden in tests
|
||||
protected virtual void NotifyListeners(ProjectChangeEventArgs e)
|
||||
{
|
||||
|
|
@ -459,5 +439,16 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
handler(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
private class Entry
|
||||
{
|
||||
public ProjectSnapshot Snapshot;
|
||||
public readonly ProjectState State;
|
||||
|
||||
public Entry(ProjectState state)
|
||||
{
|
||||
State = state;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -45,8 +45,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
return new DefaultProjectSnapshotManager(
|
||||
_foregroundDispatcher,
|
||||
languageServices.WorkspaceServices.GetRequiredService<ErrorReporter>(),
|
||||
languageServices.GetRequiredService<ProjectSnapshotWorker>(),
|
||||
_triggers,
|
||||
_triggers,
|
||||
languageServices.WorkspaceServices.Workspace);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,62 +0,0 @@
|
|||
// 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.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal class DefaultProjectSnapshotWorker : ProjectSnapshotWorker
|
||||
{
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
private readonly TagHelperResolver _tagHelperResolver;
|
||||
|
||||
public DefaultProjectSnapshotWorker(ForegroundDispatcher foregroundDispatcher, TagHelperResolver tagHelperResolver)
|
||||
{
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
if (tagHelperResolver == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(tagHelperResolver));
|
||||
}
|
||||
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
_tagHelperResolver = tagHelperResolver;
|
||||
}
|
||||
|
||||
public override Task ProcessUpdateAsync(ProjectSnapshotUpdateContext update, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (update == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(update));
|
||||
}
|
||||
|
||||
// Don't block the main thread
|
||||
if (_foregroundDispatcher.IsForegroundThread)
|
||||
{
|
||||
return Task.Factory.StartNew(ProjectUpdatesCoreAsync, update, cancellationToken, TaskCreationOptions.None, _foregroundDispatcher.BackgroundScheduler);
|
||||
}
|
||||
|
||||
return ProjectUpdatesCoreAsync(update);
|
||||
}
|
||||
|
||||
protected virtual void OnProcessingUpdate()
|
||||
{
|
||||
}
|
||||
|
||||
private async Task ProjectUpdatesCoreAsync(object state)
|
||||
{
|
||||
var update = (ProjectSnapshotUpdateContext)state;
|
||||
|
||||
OnProcessingUpdate();
|
||||
|
||||
var snapshot = new DefaultProjectSnapshot(update.HostProject, update.WorkspaceProject, update.Version);
|
||||
var result = await _tagHelperResolver.GetTagHelpersAsync(snapshot, CancellationToken.None);
|
||||
update.TagHelpers = result.Descriptors;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
// 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.Composition;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Host.Mef;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
[Shared]
|
||||
[ExportLanguageServiceFactory(typeof(ProjectSnapshotWorker), RazorLanguage.Name)]
|
||||
internal class DefaultProjectSnapshotWorkerFactory : ILanguageServiceFactory
|
||||
{
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
|
||||
[ImportingConstructor]
|
||||
public DefaultProjectSnapshotWorkerFactory(ForegroundDispatcher foregroundDispatcher)
|
||||
{
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new System.ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
}
|
||||
|
||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
||||
{
|
||||
return new DefaultProjectSnapshotWorker(_foregroundDispatcher, languageServices.GetRequiredService<TagHelperResolver>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal class DocumentGeneratedOutputTracker
|
||||
{
|
||||
// Don't keep anything if the configuration has changed. It's OK if the
|
||||
// workspace project has changes, we'll consider whether the tag helpers are
|
||||
// difference before reusing a previous result.
|
||||
private const ProjectDifference Mask = ProjectDifference.ConfigurationChanged;
|
||||
|
||||
private readonly object _lock;
|
||||
|
||||
private DocumentGeneratedOutputTracker _older;
|
||||
private Task<RazorCodeDocument> _task;
|
||||
|
||||
private IReadOnlyList<TagHelperDescriptor> _tagHelpers;
|
||||
|
||||
public DocumentGeneratedOutputTracker(DocumentGeneratedOutputTracker older)
|
||||
{
|
||||
_older = older;
|
||||
|
||||
_lock = new object();
|
||||
}
|
||||
|
||||
public bool IsResultAvailable => _task?.IsCompleted == true;
|
||||
|
||||
public Task<RazorCodeDocument> GetGeneratedOutputInitializationTask(ProjectSnapshot project, DocumentSnapshot document)
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
if (document == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(document));
|
||||
}
|
||||
|
||||
if (_task == null)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_task == null)
|
||||
{
|
||||
_task = GetGeneratedOutputInitializationTaskCore(project, document);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _task;
|
||||
}
|
||||
|
||||
public DocumentGeneratedOutputTracker ForkFor(DocumentState state, ProjectDifference difference)
|
||||
{
|
||||
if (state == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(state));
|
||||
}
|
||||
|
||||
if ((difference & Mask) != 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new DocumentGeneratedOutputTracker(this);
|
||||
}
|
||||
|
||||
private async Task<RazorCodeDocument> GetGeneratedOutputInitializationTaskCore(ProjectSnapshot project, DocumentSnapshot document)
|
||||
{
|
||||
var tagHelpers = await project.GetTagHelpersAsync().ConfigureAwait(false);
|
||||
if (_older != null && _older.IsResultAvailable)
|
||||
{
|
||||
var difference = new HashSet<TagHelperDescriptor>(TagHelperDescriptorComparer.Default);
|
||||
difference.UnionWith(_older._tagHelpers);
|
||||
difference.SymmetricExceptWith(tagHelpers);
|
||||
|
||||
if (difference.Count == 0)
|
||||
{
|
||||
// We can use the cached result.
|
||||
var result = _older._task.Result;
|
||||
|
||||
// Drop reference so it can be GC'ed
|
||||
_older = null;
|
||||
|
||||
// Cache the tag helpers so the next version can use them
|
||||
_tagHelpers = tagHelpers;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Drop reference so it can be GC'ed
|
||||
_older = null;
|
||||
|
||||
|
||||
// Cache the tag helpers so the next version can use them
|
||||
_tagHelpers = tagHelpers;
|
||||
|
||||
var projectEngine = project.GetProjectEngine();
|
||||
var projectItem = projectEngine.FileSystem.GetItem(document.FilePath);
|
||||
return projectItem == null ? null : projectEngine.ProcessDesignTime(projectItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// 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.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal abstract class DocumentSnapshot
|
||||
{
|
||||
public abstract string FilePath { get; }
|
||||
|
||||
public abstract string TargetPath { get; }
|
||||
|
||||
public abstract Task<RazorCodeDocument> GetGeneratedOutputAsync();
|
||||
|
||||
public abstract bool TryGetGeneratedOutput(out RazorCodeDocument results);
|
||||
}
|
||||
}
|
||||
|
|
@ -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 Microsoft.CodeAnalysis.Host;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal class DocumentState
|
||||
{
|
||||
private readonly object _lock;
|
||||
|
||||
private DocumentGeneratedOutputTracker _generatedOutput;
|
||||
|
||||
public DocumentState(HostWorkspaceServices services, HostDocument hostDocument)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
if (hostDocument == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hostDocument));
|
||||
}
|
||||
|
||||
Services = services;
|
||||
HostDocument = hostDocument;
|
||||
Version = VersionStamp.Create();
|
||||
|
||||
_lock = new object();
|
||||
}
|
||||
|
||||
public DocumentState(DocumentState previous, ProjectDifference difference)
|
||||
{
|
||||
if (previous == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(previous));
|
||||
}
|
||||
|
||||
Services = previous.Services;
|
||||
HostDocument = previous.HostDocument;
|
||||
Version = previous.Version.GetNewerVersion();
|
||||
|
||||
_generatedOutput = previous._generatedOutput?.ForkFor(this, difference);
|
||||
}
|
||||
|
||||
public HostDocument HostDocument { get; }
|
||||
|
||||
public HostWorkspaceServices Services { get; }
|
||||
|
||||
public VersionStamp Version { get; }
|
||||
|
||||
public DocumentGeneratedOutputTracker GeneratedOutput
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_generatedOutput == null)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_generatedOutput == null)
|
||||
{
|
||||
_generatedOutput = new DocumentGeneratedOutputTracker(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _generatedOutput;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
// 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.Host;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal class EphemeralProjectSnapshot : ProjectSnapshot
|
||||
{
|
||||
private static readonly Task<IReadOnlyList<TagHelperDescriptor>> EmptyTagHelpers = Task.FromResult<IReadOnlyList<TagHelperDescriptor>>(Array.Empty<TagHelperDescriptor>());
|
||||
|
||||
private readonly HostWorkspaceServices _services;
|
||||
private readonly Lazy<RazorProjectEngine> _projectEngine;
|
||||
|
||||
public EphemeralProjectSnapshot(HostWorkspaceServices services, string filePath)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
if (filePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(filePath));
|
||||
}
|
||||
|
||||
_services = services;
|
||||
FilePath = filePath;
|
||||
|
||||
_projectEngine = new Lazy<RazorProjectEngine>(CreateProjectEngine);
|
||||
}
|
||||
|
||||
public override RazorConfiguration Configuration => FallbackRazorConfiguration.MVC_2_1;
|
||||
|
||||
public override IEnumerable<string> DocumentFilePaths => Array.Empty<string>();
|
||||
|
||||
public override string FilePath { get; }
|
||||
|
||||
public override bool IsInitialized => false;
|
||||
|
||||
public override VersionStamp Version { get; } = VersionStamp.Default;
|
||||
|
||||
public override Project WorkspaceProject => null;
|
||||
|
||||
public override DocumentSnapshot GetDocument(string filePath)
|
||||
{
|
||||
if (filePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(filePath));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override RazorProjectEngine GetProjectEngine()
|
||||
{
|
||||
return _projectEngine.Value;
|
||||
}
|
||||
|
||||
public override Task<IReadOnlyList<TagHelperDescriptor>> GetTagHelpersAsync()
|
||||
{
|
||||
return EmptyTagHelpers;
|
||||
}
|
||||
|
||||
public override bool TryGetTagHelpers(out IReadOnlyList<TagHelperDescriptor> results)
|
||||
{
|
||||
results = EmptyTagHelpers.Result;
|
||||
return true;
|
||||
}
|
||||
|
||||
private RazorProjectEngine CreateProjectEngine()
|
||||
{
|
||||
var factory = _services.GetRequiredService<ProjectSnapshotProjectEngineFactory>();
|
||||
return factory.Create(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
// 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.Extensions.Internal;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal class HostDocument : IEquatable<HostDocument>
|
||||
{
|
||||
public HostDocument(string filePath, string targetPath)
|
||||
{
|
||||
if (filePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(filePath));
|
||||
}
|
||||
|
||||
if (targetPath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(targetPath));
|
||||
}
|
||||
|
||||
FilePath = filePath;
|
||||
TargetPath = targetPath;
|
||||
}
|
||||
|
||||
public string FilePath { get; }
|
||||
|
||||
public string TargetPath { get; }
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return base.Equals(obj as DocumentSnapshot);
|
||||
}
|
||||
|
||||
public bool Equals(DocumentSnapshot other)
|
||||
{
|
||||
if (ReferenceEquals(null, other))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return
|
||||
FilePathComparer.Instance.Equals(FilePath, other.FilePath) &&
|
||||
FilePathComparer.Instance.Equals(TargetPath, other.TargetPath);
|
||||
}
|
||||
|
||||
public bool Equals(HostDocument other)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hash = new HashCodeCombiner();
|
||||
hash.Add(FilePath, FilePathComparer.Instance);
|
||||
hash.Add(TargetPath, FilePathComparer.Instance);
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,13 +7,13 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
internal class ProjectChangeEventArgs : EventArgs
|
||||
{
|
||||
public ProjectChangeEventArgs(ProjectSnapshot project, ProjectChangeKind kind)
|
||||
public ProjectChangeEventArgs(string projectFilePath, ProjectChangeKind kind)
|
||||
{
|
||||
Project = project;
|
||||
ProjectFilePath = projectFilePath;
|
||||
Kind = kind;
|
||||
}
|
||||
|
||||
public ProjectSnapshot Project { get; }
|
||||
public string ProjectFilePath { get; }
|
||||
|
||||
public ProjectChangeKind Kind { get; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
internal enum ProjectChangeKind
|
||||
{
|
||||
Added,
|
||||
Removed,
|
||||
Changed,
|
||||
TagHelpersChanged,
|
||||
ProjectAdded,
|
||||
ProjectRemoved,
|
||||
ProjectChanged,
|
||||
DocumentsChanged,
|
||||
DocumentContentChanged,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
// 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.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
[Flags]
|
||||
internal enum ProjectDifference
|
||||
{
|
||||
None = 0,
|
||||
ConfigurationChanged = 1,
|
||||
WorkspaceProjectAdded = 2,
|
||||
WorkspaceProjectRemoved = 4,
|
||||
WorkspaceProjectChanged = 8,
|
||||
DocumentsChanged = 16,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
// 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.Host;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal class ProjectEngineTracker
|
||||
{
|
||||
private const ProjectDifference Mask = ProjectDifference.ConfigurationChanged;
|
||||
|
||||
private readonly object _lock = new object();
|
||||
|
||||
private readonly HostWorkspaceServices _services;
|
||||
private RazorProjectEngine _projectEngine;
|
||||
|
||||
public ProjectEngineTracker(ProjectState state)
|
||||
{
|
||||
if (state == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(state));
|
||||
}
|
||||
|
||||
_services = state.Services;
|
||||
}
|
||||
|
||||
public ProjectEngineTracker ForkFor(ProjectState state, ProjectDifference difference)
|
||||
{
|
||||
if (state == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(state));
|
||||
}
|
||||
|
||||
if ((difference & Mask) != 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public RazorProjectEngine GetProjectEngine(ProjectSnapshot snapshot)
|
||||
{
|
||||
if (snapshot == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(snapshot));
|
||||
}
|
||||
|
||||
if (_projectEngine == null)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_projectEngine == null)
|
||||
{
|
||||
var factory = _services.GetRequiredService<ProjectSnapshotProjectEngineFactory>();
|
||||
_projectEngine = factory.Create(snapshot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _projectEngine;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
// 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.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal sealed class ProjectExtensibilityAssembly : IEquatable<ProjectExtensibilityAssembly>
|
||||
{
|
||||
public ProjectExtensibilityAssembly(AssemblyIdentity identity)
|
||||
{
|
||||
if (identity == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(identity));
|
||||
}
|
||||
|
||||
Identity = identity;
|
||||
}
|
||||
|
||||
public AssemblyIdentity Identity { get; }
|
||||
|
||||
public bool Equals(ProjectExtensibilityAssembly other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Identity.Equals(other.Identity);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Identity.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return base.Equals(obj as ProjectExtensibilityAssembly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
|
|
@ -11,16 +11,22 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
public abstract RazorConfiguration Configuration { get; }
|
||||
|
||||
public abstract IEnumerable<string> DocumentFilePaths { get; }
|
||||
|
||||
public abstract string FilePath { get; }
|
||||
|
||||
public abstract bool IsInitialized { get; }
|
||||
|
||||
public abstract IReadOnlyList<TagHelperDescriptor> TagHelpers { get; }
|
||||
|
||||
public abstract VersionStamp Version { get; }
|
||||
|
||||
public abstract Project WorkspaceProject { get; }
|
||||
|
||||
public abstract HostProject HostProject { get; }
|
||||
public abstract RazorProjectEngine GetProjectEngine();
|
||||
|
||||
public abstract DocumentSnapshot GetDocument(string filePath);
|
||||
|
||||
public abstract Task<IReadOnlyList<TagHelperDescriptor>> GetTagHelpersAsync();
|
||||
|
||||
public abstract bool TryGetTagHelpers(out IReadOnlyList<TagHelperDescriptor> results);
|
||||
}
|
||||
}
|
||||
|
|
@ -12,5 +12,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public abstract event EventHandler<ProjectChangeEventArgs> Changed;
|
||||
|
||||
public abstract IReadOnlyList<ProjectSnapshot> Projects { get; }
|
||||
|
||||
public abstract ProjectSnapshot GetLoadedProject(string filePath);
|
||||
|
||||
public abstract ProjectSnapshot GetOrCreateProject(string filePath);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
public abstract Workspace Workspace { get; }
|
||||
|
||||
public abstract void ProjectUpdated(ProjectSnapshotUpdateContext update);
|
||||
public abstract void DocumentAdded(HostProject hostProject, HostDocument hostDocument);
|
||||
|
||||
public abstract void DocumentRemoved(HostProject hostProject, HostDocument hostDocument);
|
||||
|
||||
public abstract void HostProjectAdded(HostProject hostProject);
|
||||
|
||||
|
|
@ -17,8 +19,6 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
public abstract void HostProjectRemoved(HostProject hostProject);
|
||||
|
||||
public abstract void HostProjectBuildComplete(HostProject hostProject);
|
||||
|
||||
public abstract void WorkspaceProjectAdded(Project workspaceProject);
|
||||
|
||||
public abstract void WorkspaceProjectChanged(Project workspaceProject);
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
// 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.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal static class ProjectSnapshotManagerExtensions
|
||||
{
|
||||
public static ProjectSnapshot GetProjectWithFilePath(this ProjectSnapshotManager snapshotManager, string filePath)
|
||||
{
|
||||
var projects = snapshotManager.Projects;
|
||||
for (var i = 0; i< projects.Count; i++)
|
||||
{
|
||||
var project = projects[i];
|
||||
if (FilePathComparer.Instance.Equals(filePath, project.FilePath))
|
||||
{
|
||||
return project;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
// 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.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal class ProjectSnapshotUpdateContext
|
||||
{
|
||||
public ProjectSnapshotUpdateContext(string filePath, HostProject hostProject, Project workspaceProject, VersionStamp version)
|
||||
{
|
||||
if (filePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(filePath));
|
||||
}
|
||||
|
||||
if (hostProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hostProject));
|
||||
}
|
||||
|
||||
if (workspaceProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspaceProject));
|
||||
}
|
||||
|
||||
FilePath = filePath;
|
||||
HostProject = hostProject;
|
||||
WorkspaceProject = workspaceProject;
|
||||
Version = version;
|
||||
}
|
||||
|
||||
public string FilePath { get; }
|
||||
|
||||
public HostProject HostProject { get; }
|
||||
|
||||
public Project WorkspaceProject { get; }
|
||||
|
||||
public IReadOnlyList<TagHelperDescriptor> TagHelpers { get; set; }
|
||||
|
||||
public VersionStamp Version { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
// 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.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal abstract class ProjectSnapshotWorker : ILanguageService
|
||||
{
|
||||
public abstract Task ProcessUpdateAsync(ProjectSnapshotUpdateContext update, CancellationToken cancellationToken = default(CancellationToken));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,203 +0,0 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal class ProjectSnapshotWorkerQueue
|
||||
{
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
private readonly DefaultProjectSnapshotManager _projectManager;
|
||||
private readonly ProjectSnapshotWorker _projectWorker;
|
||||
|
||||
private readonly Dictionary<string, ProjectSnapshotUpdateContext> _projects;
|
||||
private Timer _timer;
|
||||
|
||||
public ProjectSnapshotWorkerQueue(ForegroundDispatcher foregroundDispatcher, DefaultProjectSnapshotManager projectManager, ProjectSnapshotWorker projectWorker)
|
||||
{
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
if (projectManager == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectManager));
|
||||
}
|
||||
|
||||
if (projectWorker == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectWorker));
|
||||
}
|
||||
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
_projectManager = projectManager;
|
||||
_projectWorker = projectWorker;
|
||||
|
||||
_projects = new Dictionary<string, ProjectSnapshotUpdateContext>(FilePathComparer.Instance);
|
||||
}
|
||||
|
||||
public bool HasPendingNotifications
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_projects)
|
||||
{
|
||||
return _projects.Count > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Used in unit tests to control the timer delay.
|
||||
public TimeSpan Delay { get; set; } = TimeSpan.FromSeconds(2);
|
||||
|
||||
public bool IsScheduledOrRunning => _timer != null;
|
||||
|
||||
// Used in unit tests to ensure we can control when background work starts.
|
||||
public ManualResetEventSlim BlockBackgroundWorkStart { get; set; }
|
||||
|
||||
// Used in unit tests to ensure we can know when background work finishes.
|
||||
public ManualResetEventSlim NotifyBackgroundWorkFinish { get; set; }
|
||||
|
||||
// Used in unit tests to ensure we can be notified when all completes.
|
||||
public ManualResetEventSlim NotifyForegroundWorkFinish { get; set; }
|
||||
|
||||
private void OnStartingBackgroundWork()
|
||||
{
|
||||
if (BlockBackgroundWorkStart != null)
|
||||
{
|
||||
BlockBackgroundWorkStart.Wait();
|
||||
BlockBackgroundWorkStart.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFinishingBackgroundWork()
|
||||
{
|
||||
if (NotifyBackgroundWorkFinish != null)
|
||||
{
|
||||
NotifyBackgroundWorkFinish.Set();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFinishingForegroundWork()
|
||||
{
|
||||
if (NotifyForegroundWorkFinish != null)
|
||||
{
|
||||
NotifyForegroundWorkFinish.Set();
|
||||
}
|
||||
}
|
||||
|
||||
public void Enqueue(ProjectSnapshotUpdateContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
lock (_projects)
|
||||
{
|
||||
// We only want to store the last 'seen' version of any given project. That way when we pick one to process
|
||||
// it's always the best version to use.
|
||||
_projects[context.FilePath] = context;
|
||||
|
||||
StartWorker();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void StartWorker()
|
||||
{
|
||||
// Access to the timer is protected by the lock in Enqueue and in Timer_Tick
|
||||
if (_timer == null)
|
||||
{
|
||||
// Timer will fire after a fixed delay, but only once.
|
||||
_timer = new Timer(Timer_Tick, null, Delay, Timeout.InfiniteTimeSpan);
|
||||
}
|
||||
}
|
||||
|
||||
private async void Timer_Tick(object state) // Yeah I know.
|
||||
{
|
||||
try
|
||||
{
|
||||
_foregroundDispatcher.AssertBackgroundThread();
|
||||
|
||||
// Timer is stopped.
|
||||
_timer.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
|
||||
OnStartingBackgroundWork();
|
||||
|
||||
ProjectSnapshotUpdateContext[] work;
|
||||
lock (_projects)
|
||||
{
|
||||
work = _projects.Values.ToArray();
|
||||
_projects.Clear();
|
||||
}
|
||||
|
||||
var updates = new(ProjectSnapshotUpdateContext context, Exception exception)[work.Length];
|
||||
for (var i = 0; i < work.Length; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
updates[i] = (work[i], null);
|
||||
await _projectWorker.ProcessUpdateAsync(updates[i].context);
|
||||
}
|
||||
catch (Exception projectException)
|
||||
{
|
||||
updates[i] = (updates[i].context, projectException);
|
||||
}
|
||||
}
|
||||
|
||||
OnFinishingBackgroundWork();
|
||||
|
||||
// We need to get back to the UI thread to update the project system.
|
||||
await Task.Factory.StartNew(PersistUpdates, updates, CancellationToken.None, TaskCreationOptions.None, _foregroundDispatcher.ForegroundScheduler);
|
||||
|
||||
lock (_projects)
|
||||
{
|
||||
// Resetting the timer allows another batch of work to start.
|
||||
_timer.Dispose();
|
||||
_timer = null;
|
||||
|
||||
// If more work came in while we were running start the worker again.
|
||||
if (_projects.Count > 0)
|
||||
{
|
||||
StartWorker();
|
||||
}
|
||||
}
|
||||
|
||||
OnFinishingForegroundWork();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// This is something totally unexpected, let's just send it over to the workspace.
|
||||
await Task.Factory.StartNew(() => _projectManager.ReportError(ex), CancellationToken.None, TaskCreationOptions.None, _foregroundDispatcher.ForegroundScheduler);
|
||||
}
|
||||
}
|
||||
|
||||
private void PersistUpdates(object state)
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
var updates = ((ProjectSnapshotUpdateContext context, Exception exception)[])state;
|
||||
|
||||
for (var i = 0; i < updates.Length; i++)
|
||||
{
|
||||
var update = updates[i];
|
||||
if (update.exception == null)
|
||||
{
|
||||
_projectManager.ProjectUpdated(update.context);
|
||||
}
|
||||
else
|
||||
{
|
||||
_projectManager.ReportError(update.exception, update.context?.WorkspaceProject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,239 @@
|
|||
// 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.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
// Internal tracker for DefaultProjectSnapshot
|
||||
internal class ProjectState
|
||||
{
|
||||
private static readonly IReadOnlyDictionary<string, DocumentState> EmptyDocuments = new Dictionary<string, DocumentState>();
|
||||
|
||||
private readonly object _lock;
|
||||
|
||||
private ProjectEngineTracker _projectEngine;
|
||||
private ProjectTagHelperTracker _tagHelpers;
|
||||
|
||||
public ProjectState(
|
||||
HostWorkspaceServices services,
|
||||
HostProject hostProject,
|
||||
Project workspaceProject)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
if (hostProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hostProject));
|
||||
}
|
||||
|
||||
Services = services;
|
||||
HostProject = hostProject;
|
||||
WorkspaceProject = workspaceProject;
|
||||
Documents = EmptyDocuments;
|
||||
Version = VersionStamp.Create();
|
||||
|
||||
_lock = new object();
|
||||
}
|
||||
|
||||
public ProjectState(
|
||||
ProjectState older,
|
||||
ProjectDifference difference,
|
||||
HostProject hostProject,
|
||||
Project workspaceProject,
|
||||
IReadOnlyDictionary<string, DocumentState> documents)
|
||||
{
|
||||
if (older == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(older));
|
||||
}
|
||||
|
||||
if (hostProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hostProject));
|
||||
}
|
||||
|
||||
if (documents == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documents));
|
||||
}
|
||||
|
||||
Services = older.Services;
|
||||
Version = older.Version.GetNewerVersion();
|
||||
|
||||
HostProject = hostProject;
|
||||
WorkspaceProject = workspaceProject;
|
||||
Documents = documents;
|
||||
|
||||
_lock = new object();
|
||||
|
||||
_projectEngine = older._projectEngine?.ForkFor(this, difference);
|
||||
_tagHelpers = older._tagHelpers?.ForkFor(this, difference);
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<string, DocumentState> Documents { get; }
|
||||
|
||||
public HostProject HostProject { get; }
|
||||
|
||||
public HostWorkspaceServices Services { get; }
|
||||
|
||||
public Project WorkspaceProject { get; }
|
||||
|
||||
public VersionStamp Version { get; }
|
||||
|
||||
// Computed State
|
||||
public ProjectEngineTracker ProjectEngine
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_projectEngine == null)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_projectEngine == null)
|
||||
{
|
||||
_projectEngine = new ProjectEngineTracker(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _projectEngine;
|
||||
}
|
||||
}
|
||||
|
||||
// Computed State
|
||||
public ProjectTagHelperTracker TagHelpers
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_tagHelpers == null)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_tagHelpers == null)
|
||||
{
|
||||
_tagHelpers = new ProjectTagHelperTracker(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _tagHelpers;
|
||||
}
|
||||
}
|
||||
|
||||
public ProjectState AddHostDocument(HostDocument hostDocument)
|
||||
{
|
||||
if (hostDocument == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hostDocument));
|
||||
}
|
||||
|
||||
// Ignore attempts to 'add' a document with different data, we only
|
||||
// care about one, so it might as well be the one we have.
|
||||
if (Documents.ContainsKey(hostDocument.FilePath))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
var documents = new Dictionary<string, DocumentState>(FilePathComparer.Instance);
|
||||
foreach (var kvp in Documents)
|
||||
{
|
||||
documents.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
documents.Add(hostDocument.FilePath, new DocumentState(Services, hostDocument));
|
||||
|
||||
var difference = ProjectDifference.DocumentsChanged;
|
||||
var state = new ProjectState(this, difference, HostProject, WorkspaceProject, documents);
|
||||
return state;
|
||||
}
|
||||
|
||||
public ProjectState RemoveHostDocument(HostDocument hostDocument)
|
||||
{
|
||||
if (hostDocument == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hostDocument));
|
||||
}
|
||||
|
||||
if (!Documents.ContainsKey(hostDocument.FilePath))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
var documents = new Dictionary<string, DocumentState>(FilePathComparer.Instance);
|
||||
foreach (var kvp in Documents)
|
||||
{
|
||||
documents.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
documents.Remove(hostDocument.FilePath);
|
||||
|
||||
var difference = ProjectDifference.DocumentsChanged;
|
||||
var state = new ProjectState(this, difference, HostProject, WorkspaceProject, documents);
|
||||
return state;
|
||||
}
|
||||
|
||||
public ProjectState WithHostProject(HostProject hostProject)
|
||||
{
|
||||
if (hostProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hostProject));
|
||||
}
|
||||
|
||||
if (HostProject.Configuration.Equals(hostProject.Configuration))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
var difference = ProjectDifference.ConfigurationChanged;
|
||||
var documents = new Dictionary<string, DocumentState>(FilePathComparer.Instance);
|
||||
foreach (var kvp in Documents)
|
||||
{
|
||||
documents.Add(kvp.Key, new DocumentState(kvp.Value, difference));
|
||||
}
|
||||
|
||||
var state = new ProjectState(this, difference, hostProject, WorkspaceProject, documents);
|
||||
return state;
|
||||
}
|
||||
|
||||
public ProjectState WithWorkspaceProject(Project workspaceProject)
|
||||
{
|
||||
var difference = ProjectDifference.None;
|
||||
if (WorkspaceProject == null && workspaceProject != null)
|
||||
{
|
||||
difference |= ProjectDifference.WorkspaceProjectAdded;
|
||||
}
|
||||
else if (WorkspaceProject != null && workspaceProject == null)
|
||||
{
|
||||
difference |= ProjectDifference.WorkspaceProjectRemoved;
|
||||
}
|
||||
else if (
|
||||
WorkspaceProject?.Id != workspaceProject?.Id ||
|
||||
WorkspaceProject?.Version != workspaceProject?.Version)
|
||||
{
|
||||
// For now this is very naive. We will want to consider changing
|
||||
// our logic here to be more robust.
|
||||
difference |= ProjectDifference.WorkspaceProjectChanged;
|
||||
}
|
||||
|
||||
if (difference == ProjectDifference.None)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
var documents = new Dictionary<string, DocumentState>(FilePathComparer.Instance);
|
||||
foreach (var kvp in Documents)
|
||||
{
|
||||
documents.Add(kvp.Key, new DocumentState(kvp.Value, difference));
|
||||
}
|
||||
|
||||
var state = new ProjectState(this, difference, HostProject, workspaceProject, documents);
|
||||
return state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
// 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.Host;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal class ProjectTagHelperTracker
|
||||
{
|
||||
private const ProjectDifference Mask =
|
||||
ProjectDifference.ConfigurationChanged |
|
||||
ProjectDifference.WorkspaceProjectAdded |
|
||||
ProjectDifference.WorkspaceProjectChanged |
|
||||
ProjectDifference.WorkspaceProjectRemoved;
|
||||
|
||||
private readonly object _lock = new object();
|
||||
private readonly HostWorkspaceServices _services;
|
||||
|
||||
private Task<IReadOnlyList<TagHelperDescriptor>> _task;
|
||||
|
||||
public ProjectTagHelperTracker(ProjectState state)
|
||||
{
|
||||
if (state == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(state));
|
||||
}
|
||||
|
||||
_services = state.Services;
|
||||
}
|
||||
|
||||
public bool IsResultAvailable => _task?.IsCompleted == true;
|
||||
|
||||
public ProjectTagHelperTracker ForkFor(ProjectState state, ProjectDifference difference)
|
||||
{
|
||||
if (state == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(state));
|
||||
}
|
||||
|
||||
if ((difference & Mask) != 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<TagHelperDescriptor>> GetTagHelperInitializationTask(ProjectSnapshot snapshot)
|
||||
{
|
||||
if (snapshot == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(snapshot));
|
||||
}
|
||||
|
||||
if (_task == null)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_task == null)
|
||||
{
|
||||
_task = GetTagHelperInitializationTaskCore(snapshot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _task;
|
||||
}
|
||||
|
||||
private async Task<IReadOnlyList<TagHelperDescriptor>> GetTagHelperInitializationTaskCore(ProjectSnapshot snapshot)
|
||||
{
|
||||
var resolver = _services.GetLanguageServices(RazorLanguage.Name).GetRequiredService<TagHelperResolver>();
|
||||
return (await resolver.GetTagHelpersAsync(snapshot)).Descriptors;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
// 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.Host;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
internal abstract class RazorProjectEngineFactoryService : ILanguageService
|
||||
{
|
||||
public abstract IProjectEngineFactory FindFactory(ProjectSnapshot project);
|
||||
|
||||
public abstract IProjectEngineFactory FindSerializableFactory(ProjectSnapshot project);
|
||||
|
||||
public abstract RazorProjectEngine Create(ProjectSnapshot project, Action<RazorProjectEngineBuilder> configure);
|
||||
|
||||
public abstract RazorProjectEngine Create(ProjectSnapshot project, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure);
|
||||
|
||||
public abstract RazorProjectEngine Create(string directoryPath, Action<RazorProjectEngineBuilder> configure);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
// 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.CodeAnalysis.Remote.Razor
|
||||
{
|
||||
internal class GeneratedDocument
|
||||
{
|
||||
public string Text { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -2,13 +2,9 @@
|
|||
// 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.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
|
|
@ -27,42 +23,5 @@ namespace Microsoft.CodeAnalysis.Remote.Razor
|
|||
|
||||
return await RazorServices.TagHelperResolver.GetTagHelpersAsync(project, factoryTypeName, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<IEnumerable<DirectiveDescriptor>> GetDirectivesAsync(Guid projectIdBytes, string projectDebugName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var projectId = ProjectId.CreateFromSerialized(projectIdBytes, projectDebugName);
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create();
|
||||
var directives = projectEngine.EngineFeatures.OfType<IRazorDirectiveFeature>().FirstOrDefault()?.Directives;
|
||||
return Task.FromResult(directives ?? Enumerable.Empty<DirectiveDescriptor>());
|
||||
}
|
||||
|
||||
public Task<GeneratedDocument> GenerateDocumentAsync(Guid projectIdBytes, string projectDebugName, string filePath, string text, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var projectId = ProjectId.CreateFromSerialized(projectIdBytes, projectDebugName);
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create();
|
||||
|
||||
RazorSourceDocument source;
|
||||
using (var stream = new MemoryStream())
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes(text);
|
||||
stream.Write(bytes, 0, bytes.Length);
|
||||
|
||||
stream.Seek(0L, SeekOrigin.Begin);
|
||||
source = RazorSourceDocument.ReadFrom(stream, filePath, Encoding.UTF8);
|
||||
}
|
||||
|
||||
var code = RazorCodeDocument.Create(source);
|
||||
projectEngine.Engine.Process(code);
|
||||
|
||||
var csharp = code.GetCSharpDocument();
|
||||
if (csharp == null)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
return Task.FromResult(new GeneratedDocument() { Text = csharp.GeneratedCode, });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,9 +48,7 @@ namespace Microsoft.CodeAnalysis.Remote.Razor
|
|||
{
|
||||
FilePath = filePath;
|
||||
Configuration = configuration;
|
||||
HostProject = new HostProject(filePath, configuration);
|
||||
WorkspaceProject = workspaceProject;
|
||||
TagHelpers = Array.Empty<TagHelperDescriptor>();
|
||||
|
||||
IsInitialized = true;
|
||||
Version = VersionStamp.Default;
|
||||
|
|
@ -58,6 +56,8 @@ namespace Microsoft.CodeAnalysis.Remote.Razor
|
|||
|
||||
public override RazorConfiguration Configuration { get; }
|
||||
|
||||
public override IEnumerable<string> DocumentFilePaths => Array.Empty<string>();
|
||||
|
||||
public override string FilePath { get; }
|
||||
|
||||
public override bool IsInitialized { get; }
|
||||
|
|
@ -66,9 +66,30 @@ namespace Microsoft.CodeAnalysis.Remote.Razor
|
|||
|
||||
public override Project WorkspaceProject { get; }
|
||||
|
||||
public override HostProject HostProject { get; }
|
||||
public override DocumentSnapshot GetDocument(string filePath)
|
||||
{
|
||||
if (filePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(filePath));
|
||||
}
|
||||
|
||||
public override IReadOnlyList<TagHelperDescriptor> TagHelpers { get; }
|
||||
return null;
|
||||
}
|
||||
|
||||
public override RazorProjectEngine GetProjectEngine()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override Task<IReadOnlyList<TagHelperDescriptor>> GetTagHelpersAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override bool TryGetTagHelpers(out IReadOnlyList<TagHelperDescriptor> results)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Rule
|
||||
Description="Razor Document Properties"
|
||||
DisplayName="Razor Document Properties"
|
||||
Name="RazorGenerateWithTargetPath"
|
||||
PageTemplate="generic"
|
||||
xmlns="http://schemas.microsoft.com/build/2009/properties">
|
||||
<Rule.DataSource>
|
||||
<DataSource
|
||||
Persistence="ProjectFile"
|
||||
ItemType="RazorGenerateWithTargetPath"
|
||||
MSBuildTarget="RazorGenerateDesignTime"
|
||||
HasConfigurationCondition="False"
|
||||
SourceOfDefaultValue="AfterContext"
|
||||
SourceType="TargetResults" />
|
||||
</Rule.DataSource>
|
||||
|
||||
<Rule.Categories>
|
||||
<Category
|
||||
Name="General"
|
||||
DisplayName="General" />
|
||||
</Rule.Categories>
|
||||
|
||||
<StringProperty
|
||||
Category="General"
|
||||
Name="TargetPath"
|
||||
ReadOnly="True"
|
||||
Visible="False" />
|
||||
|
||||
</Rule>
|
||||
|
|
@ -16,7 +16,6 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
private readonly FileChangeTrackerFactory _fileChangeTrackerFactory;
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
private readonly ErrorReporter _errorReporter;
|
||||
private readonly RazorProjectEngineFactoryService _projectEngineFactoryService;
|
||||
private readonly Dictionary<string, ImportTracker> _importTrackerCache;
|
||||
|
||||
public override event EventHandler<ImportChangedEventArgs> Changed;
|
||||
|
|
@ -24,8 +23,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
public DefaultImportDocumentManager(
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
ErrorReporter errorReporter,
|
||||
FileChangeTrackerFactory fileChangeTrackerFactory,
|
||||
RazorProjectEngineFactoryService projectEngineFactoryService)
|
||||
FileChangeTrackerFactory fileChangeTrackerFactory)
|
||||
{
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
|
|
@ -42,15 +40,9 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
throw new ArgumentNullException(nameof(fileChangeTrackerFactory));
|
||||
}
|
||||
|
||||
if (projectEngineFactoryService == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectEngineFactoryService));
|
||||
}
|
||||
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
_errorReporter = errorReporter;
|
||||
_fileChangeTrackerFactory = fileChangeTrackerFactory;
|
||||
_projectEngineFactoryService = projectEngineFactoryService;
|
||||
_importTrackerCache = new Dictionary<string, ImportTracker>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
|
|
@ -115,8 +107,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
|
||||
private IEnumerable<RazorProjectItem> GetImportItems(VisualStudioDocumentTracker tracker)
|
||||
{
|
||||
var projectDirectory = Path.GetDirectoryName(tracker.ProjectPath);
|
||||
var projectEngine = _projectEngineFactoryService.Create(projectDirectory, _ => { });
|
||||
var projectEngine = tracker.ProjectSnapshot.GetProjectEngine();
|
||||
var trackerItem = projectEngine.FileSystem.GetItem(tracker.FilePath);
|
||||
var importFeature = projectEngine.ProjectFeatures.OfType<IImportProjectFeature>().FirstOrDefault();
|
||||
|
||||
|
|
|
|||
|
|
@ -35,13 +35,8 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
|
||||
var errorReporter = languageServices.WorkspaceServices.GetRequiredService<ErrorReporter>();
|
||||
var fileChangeTrackerFactory = languageServices.GetRequiredService<FileChangeTrackerFactory>();
|
||||
var projectEngineFactoryService = languageServices.GetRequiredService<RazorProjectEngineFactoryService>();
|
||||
|
||||
return new DefaultImportDocumentManager(
|
||||
_foregroundDispatcher,
|
||||
errorReporter,
|
||||
fileChangeTrackerFactory,
|
||||
projectEngineFactoryService);
|
||||
return new DefaultImportDocumentManager(_foregroundDispatcher, errorReporter, fileChangeTrackerFactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,196 +0,0 @@
|
|||
// 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.IO;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor
|
||||
{
|
||||
internal class DefaultProjectEngineFactoryService : RazorProjectEngineFactoryService
|
||||
{
|
||||
private readonly static RazorConfiguration DefaultConfiguration = FallbackRazorConfiguration.MVC_2_1;
|
||||
|
||||
private readonly Workspace _workspace;
|
||||
private readonly IFallbackProjectEngineFactory _defaultFactory;
|
||||
private readonly Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[] _customFactories;
|
||||
private ProjectSnapshotManager _projectManager;
|
||||
|
||||
public DefaultProjectEngineFactoryService(
|
||||
Workspace workspace,
|
||||
IFallbackProjectEngineFactory defaultFactory,
|
||||
Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[] customFactories)
|
||||
{
|
||||
if (workspace == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspace));
|
||||
}
|
||||
|
||||
if (defaultFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(defaultFactory));
|
||||
}
|
||||
|
||||
if (customFactories == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(customFactories));
|
||||
}
|
||||
|
||||
_workspace = workspace;
|
||||
_defaultFactory = defaultFactory;
|
||||
_customFactories = customFactories;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal DefaultProjectEngineFactoryService(
|
||||
ProjectSnapshotManager projectManager,
|
||||
IFallbackProjectEngineFactory defaultFactory,
|
||||
Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[] customFactories)
|
||||
{
|
||||
if (projectManager == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectManager));
|
||||
}
|
||||
|
||||
if (defaultFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(defaultFactory));
|
||||
}
|
||||
|
||||
if (customFactories == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(customFactories));
|
||||
}
|
||||
|
||||
_projectManager = projectManager;
|
||||
_defaultFactory = defaultFactory;
|
||||
_customFactories = customFactories;
|
||||
}
|
||||
|
||||
public override IProjectEngineFactory FindFactory(ProjectSnapshot project)
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
return SelectFactory(project.Configuration ?? DefaultConfiguration, requireSerializable: false);
|
||||
}
|
||||
|
||||
public override IProjectEngineFactory FindSerializableFactory(ProjectSnapshot project)
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
return SelectFactory(project.Configuration ?? DefaultConfiguration, requireSerializable: true);
|
||||
}
|
||||
|
||||
public override RazorProjectEngine Create(ProjectSnapshot project, Action<RazorProjectEngineBuilder> configure)
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
return CreateCore(project, RazorProjectFileSystem.Create(Path.GetDirectoryName(project.FilePath)), configure);
|
||||
}
|
||||
|
||||
public override RazorProjectEngine Create(string directoryPath, Action<RazorProjectEngineBuilder> configure)
|
||||
{
|
||||
if (directoryPath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(directoryPath));
|
||||
}
|
||||
|
||||
var project = FindProjectByDirectory(directoryPath);
|
||||
return CreateCore(project, RazorProjectFileSystem.Create(directoryPath), configure);
|
||||
}
|
||||
|
||||
public override RazorProjectEngine Create(ProjectSnapshot project, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
if (fileSystem == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(fileSystem));
|
||||
}
|
||||
|
||||
return CreateCore(project, fileSystem, configure);
|
||||
}
|
||||
|
||||
private RazorProjectEngine CreateCore(ProjectSnapshot project, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
|
||||
{
|
||||
// When we're running in the editor, the editor provides a configure delegate that will include
|
||||
// the editor settings and tag helpers.
|
||||
//
|
||||
// This service is only used in process in Visual Studio, and any other callers should provide these
|
||||
// things also.
|
||||
configure = configure ?? ((b) => { });
|
||||
|
||||
// The default configuration currently matches the newest MVC configuration.
|
||||
//
|
||||
// We typically want this because the language adds features over time - we don't want to a bunch of errors
|
||||
// to show up when a document is first opened, and then go away when the configuration loads, we'd prefer the opposite.
|
||||
var configuration = project?.Configuration ?? DefaultConfiguration;
|
||||
|
||||
// If there's no factory to handle the configuration then fall back to a very basic configuration.
|
||||
//
|
||||
// This will stop a crash from happening in this case (misconfigured project), but will still make
|
||||
// it obvious to the user that something is wrong.
|
||||
var factory = SelectFactory(configuration) ?? _defaultFactory;
|
||||
return factory.Create(configuration, fileSystem, configure);
|
||||
}
|
||||
|
||||
private IProjectEngineFactory SelectFactory(RazorConfiguration configuration, bool requireSerializable = false)
|
||||
{
|
||||
for (var i = 0; i < _customFactories.Length; i++)
|
||||
{
|
||||
var factory = _customFactories[i];
|
||||
if (string.Equals(configuration.ConfigurationName, factory.Metadata.ConfigurationName))
|
||||
{
|
||||
return requireSerializable && !factory.Metadata.SupportsSerialization ? null : factory.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private ProjectSnapshot FindProjectByDirectory(string directory)
|
||||
{
|
||||
directory = NormalizeDirectoryPath(directory);
|
||||
|
||||
if (_projectManager == null)
|
||||
{
|
||||
_projectManager = _workspace.Services.GetLanguageServices(RazorLanguage.Name).GetRequiredService<ProjectSnapshotManager>();
|
||||
}
|
||||
|
||||
var projects = _projectManager.Projects;
|
||||
for (var i = 0; i < projects.Count; i++)
|
||||
{
|
||||
var project = projects[i];
|
||||
if (project.FilePath != null)
|
||||
{
|
||||
if (string.Equals(directory, NormalizeDirectoryPath(Path.GetDirectoryName(project.FilePath)), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return project;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string NormalizeDirectoryPath(string path)
|
||||
{
|
||||
return path.Replace('\\', '/').TrimEnd('/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
// 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.Composition;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Host.Mef;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor
|
||||
{
|
||||
[ExportLanguageServiceFactory(typeof(RazorProjectEngineFactoryService), RazorLanguage.Name, ServiceLayer.Default)]
|
||||
internal class DefaultProjectEngineFactoryServiceFactory : ILanguageServiceFactory
|
||||
{
|
||||
private readonly Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[] _customFactories;
|
||||
private readonly IFallbackProjectEngineFactory _fallbackFactory;
|
||||
|
||||
[ImportingConstructor]
|
||||
public DefaultProjectEngineFactoryServiceFactory(
|
||||
IFallbackProjectEngineFactory fallbackFactory,
|
||||
[ImportMany] IEnumerable<Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>> customFactories)
|
||||
{
|
||||
if (fallbackFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(fallbackFactory));
|
||||
}
|
||||
|
||||
if (customFactories == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(customFactories));
|
||||
}
|
||||
|
||||
_fallbackFactory = fallbackFactory;
|
||||
_customFactories = customFactories.ToArray();
|
||||
}
|
||||
|
||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
||||
{
|
||||
return new DefaultProjectEngineFactoryService(
|
||||
languageServices.WorkspaceServices.Workspace,
|
||||
_fallbackFactory,
|
||||
_customFactories);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
|
|
@ -12,18 +11,6 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
{
|
||||
internal class DefaultTagHelperResolver : TagHelperResolver
|
||||
{
|
||||
private readonly RazorProjectEngineFactoryService _engineFactory;
|
||||
|
||||
public DefaultTagHelperResolver(RazorProjectEngineFactoryService engineFactory)
|
||||
{
|
||||
if (engineFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(engineFactory));
|
||||
}
|
||||
|
||||
_engineFactory = engineFactory;
|
||||
}
|
||||
|
||||
public override Task<TagHelperResolutionResult> GetTagHelpersAsync(ProjectSnapshot project, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (project == null)
|
||||
|
|
@ -35,9 +22,8 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
{
|
||||
return Task.FromResult(TagHelperResolutionResult.Empty);
|
||||
}
|
||||
|
||||
var engine = _engineFactory.Create(project, RazorProjectFileSystem.Empty, b => { });
|
||||
return GetTagHelpersAsync(project, engine);
|
||||
|
||||
return GetTagHelpersAsync(project, project.GetProjectEngine());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
{
|
||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
||||
{
|
||||
return new DefaultTagHelperResolver(languageServices.GetRequiredService<RazorProjectEngineFactoryService>());
|
||||
return new DefaultTagHelperResolver();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,9 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
|
|
@ -25,7 +28,13 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
private readonly List<ITextView> _textViews;
|
||||
private readonly Workspace _workspace;
|
||||
private bool _isSupportedProject;
|
||||
private ProjectSnapshot _project;
|
||||
private ProjectSnapshot _projectSnapshot;
|
||||
|
||||
// Only allow a single tag helper computation task at a time.
|
||||
private (ProjectSnapshot project, Task task) _computingTagHelpers;
|
||||
|
||||
// Stores the result from the last time we computed tag helpers.
|
||||
private IReadOnlyList<TagHelperDescriptor> _tagHelpers;
|
||||
|
||||
public override event EventHandler<ContextChangeEventArgs> ContextChanged;
|
||||
|
||||
|
|
@ -89,17 +98,23 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
_workspace = workspace; // For now we assume that the workspace is the always default VS workspace.
|
||||
|
||||
_textViews = new List<ITextView>();
|
||||
_tagHelpers = Array.Empty<TagHelperDescriptor>();
|
||||
}
|
||||
|
||||
public override RazorConfiguration Configuration => _project?.Configuration;
|
||||
public override RazorConfiguration Configuration => _projectSnapshot?.Configuration;
|
||||
|
||||
public override EditorSettings EditorSettings => _workspaceEditorSettings.Current;
|
||||
|
||||
public override IReadOnlyList<TagHelperDescriptor> TagHelpers => _project?.TagHelpers ?? Array.Empty<TagHelperDescriptor>();
|
||||
public override IReadOnlyList<TagHelperDescriptor> TagHelpers => _tagHelpers;
|
||||
|
||||
public override bool IsSupportedProject => _isSupportedProject;
|
||||
|
||||
public override Project Project => _workspace.CurrentSolution.GetProject(_project.WorkspaceProject.Id);
|
||||
public override Project Project =>
|
||||
_projectSnapshot.WorkspaceProject == null ?
|
||||
null :
|
||||
_workspace.CurrentSolution.GetProject(_projectSnapshot.WorkspaceProject.Id);
|
||||
|
||||
internal override ProjectSnapshot ProjectSnapshot => _projectSnapshot;
|
||||
|
||||
public override ITextBuffer TextBuffer => _textBuffer;
|
||||
|
||||
|
|
@ -111,6 +126,8 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
|
||||
public override string ProjectPath => _projectPath;
|
||||
|
||||
public Task PendingTagHelperTask => _computingTagHelpers.task ?? Task.CompletedTask;
|
||||
|
||||
internal void AddTextView(ITextView textView)
|
||||
{
|
||||
if (textView == null)
|
||||
|
|
@ -118,6 +135,8 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
throw new ArgumentNullException(nameof(textView));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
if (!_textViews.Contains(textView))
|
||||
{
|
||||
_textViews.Add(textView);
|
||||
|
|
@ -131,6 +150,8 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
throw new ArgumentNullException(nameof(textView));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
if (_textViews.Contains(textView))
|
||||
{
|
||||
_textViews.Remove(textView);
|
||||
|
|
@ -139,6 +160,8 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
|
||||
public override ITextView GetFocusedTextView()
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
for (var i = 0; i < TextViews.Count; i++)
|
||||
{
|
||||
if (TextViews[i].HasAggregateFocus)
|
||||
|
|
@ -152,20 +175,23 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
|
||||
public void Subscribe()
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
_projectSnapshot = _projectManager.GetOrCreateProject(_projectPath);
|
||||
_isSupportedProject = true;
|
||||
|
||||
_projectManager.Changed += ProjectManager_Changed;
|
||||
_workspaceEditorSettings.Changed += EditorSettingsManager_Changed;
|
||||
_importDocumentManager.Changed += Import_Changed;
|
||||
_importDocumentManager.OnSubscribed(this);
|
||||
|
||||
_workspaceEditorSettings.Changed += EditorSettingsManager_Changed;
|
||||
_projectManager.Changed += ProjectManager_Changed;
|
||||
_importDocumentManager.Changed += Import_Changed;
|
||||
|
||||
_isSupportedProject = true;
|
||||
_project = _projectManager.GetProjectWithFilePath(_projectPath);
|
||||
|
||||
OnContextChanged(_project, ContextChangeKind.ProjectChanged);
|
||||
OnContextChanged(ContextChangeKind.ProjectChanged);
|
||||
}
|
||||
|
||||
public void Unsubscribe()
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
_importDocumentManager.OnUnsubscribed(this);
|
||||
|
||||
_projectManager.Changed -= ProjectManager_Changed;
|
||||
|
|
@ -174,37 +200,112 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
|
||||
// Detached from project.
|
||||
_isSupportedProject = false;
|
||||
_project = null;
|
||||
|
||||
OnContextChanged(project: null, kind: ContextChangeKind.ProjectChanged);
|
||||
_projectSnapshot = null;
|
||||
OnContextChanged(kind: ContextChangeKind.ProjectChanged);
|
||||
}
|
||||
|
||||
private void OnContextChanged(ProjectSnapshot project, ContextChangeKind kind)
|
||||
private void StartComputingTagHelpers()
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
_project = project;
|
||||
Debug.Assert(_projectSnapshot != null);
|
||||
Debug.Assert(_computingTagHelpers.project == null && _computingTagHelpers.task == null);
|
||||
|
||||
if (_projectSnapshot.TryGetTagHelpers(out var results))
|
||||
{
|
||||
_tagHelpers = results;
|
||||
OnContextChanged(ContextChangeKind.TagHelpersChanged);
|
||||
return;
|
||||
}
|
||||
|
||||
// if we get here then we know the tag helpers aren't available, so force async for ease of testing
|
||||
var task = _projectSnapshot
|
||||
.GetTagHelpersAsync()
|
||||
.ContinueWith(TagHelpersUpdated, CancellationToken.None, TaskContinuationOptions.RunContinuationsAsynchronously, _foregroundDispatcher.ForegroundScheduler);
|
||||
_computingTagHelpers = (_projectSnapshot, task);
|
||||
}
|
||||
|
||||
private void TagHelpersUpdated(Task<IReadOnlyList<TagHelperDescriptor>> task)
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
Debug.Assert(_computingTagHelpers.project != null && _computingTagHelpers.task != null);
|
||||
|
||||
if (!_isSupportedProject)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_tagHelpers = task.Exception == null ? task.Result : Array.Empty<TagHelperDescriptor>();
|
||||
OnContextChanged(ContextChangeKind.TagHelpersChanged);
|
||||
|
||||
var projectHasChanges = _projectSnapshot != null && _projectSnapshot != _computingTagHelpers.project;
|
||||
_computingTagHelpers = (null, null);
|
||||
|
||||
if (projectHasChanges)
|
||||
{
|
||||
// More changes, keep going.
|
||||
StartComputingTagHelpers();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnContextChanged(ContextChangeKind kind)
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
var handler = ContextChanged;
|
||||
if (handler != null)
|
||||
{
|
||||
handler(this, new ContextChangeEventArgs(kind));
|
||||
}
|
||||
|
||||
if (kind == ContextChangeKind.ProjectChanged &&
|
||||
_projectSnapshot != null &&
|
||||
_computingTagHelpers.project == null)
|
||||
{
|
||||
StartComputingTagHelpers();
|
||||
}
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal void ProjectManager_Changed(object sender, ProjectChangeEventArgs e)
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
if (_projectPath != null &&
|
||||
string.Equals(_projectPath, e.Project.FilePath, StringComparison.OrdinalIgnoreCase))
|
||||
string.Equals(_projectPath, e.ProjectFilePath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (e.Kind == ProjectChangeKind.TagHelpersChanged)
|
||||
// This will be the new snapshot unless the project was removed.
|
||||
_projectSnapshot = _projectManager.GetLoadedProject(e.ProjectFilePath);
|
||||
|
||||
switch (e.Kind)
|
||||
{
|
||||
OnContextChanged(e.Project, ContextChangeKind.TagHelpersChanged);
|
||||
}
|
||||
else
|
||||
{
|
||||
OnContextChanged(e.Project, ContextChangeKind.ProjectChanged);
|
||||
case ProjectChangeKind.DocumentsChanged:
|
||||
|
||||
// Nothing to do.
|
||||
break;
|
||||
|
||||
case ProjectChangeKind.ProjectAdded:
|
||||
case ProjectChangeKind.ProjectChanged:
|
||||
|
||||
// Just an update
|
||||
OnContextChanged(ContextChangeKind.ProjectChanged);
|
||||
break;
|
||||
|
||||
case ProjectChangeKind.ProjectRemoved:
|
||||
|
||||
// Fall back to ephemeral project
|
||||
_projectSnapshot = _projectManager.GetOrCreateProject(ProjectPath);
|
||||
OnContextChanged(ContextChangeKind.ProjectChanged);
|
||||
break;
|
||||
|
||||
case ProjectChangeKind.DocumentContentChanged:
|
||||
|
||||
// Do nothing
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException($"Unknown ProjectChangeKind {e.Kind}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -212,17 +313,21 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
// Internal for testing
|
||||
internal void EditorSettingsManager_Changed(object sender, EditorSettingsChangedEventArgs args)
|
||||
{
|
||||
OnContextChanged(_project, ContextChangeKind.EditorSettingsChanged);
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
OnContextChanged(ContextChangeKind.EditorSettingsChanged);
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal void Import_Changed(object sender, ImportChangedEventArgs args)
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
foreach (var path in args.AssociatedDocuments)
|
||||
{
|
||||
if (string.Equals(_filePath, path, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
OnContextChanged(_project, ContextChangeKind.ImportsChanged);
|
||||
OnContextChanged(ContextChangeKind.ImportsChanged);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -31,7 +32,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
private readonly VisualStudioCompletionBroker _completionBroker;
|
||||
private readonly VisualStudioDocumentTracker _documentTracker;
|
||||
private readonly ForegroundDispatcher _dispatcher;
|
||||
private readonly RazorProjectEngineFactoryService _projectEngineFactory;
|
||||
private readonly ProjectSnapshotProjectEngineFactory _projectEngineFactory;
|
||||
private readonly ErrorReporter _errorReporter;
|
||||
private RazorProjectEngine _projectEngine;
|
||||
private RazorCodeDocument _codeDocument;
|
||||
|
|
@ -47,7 +48,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
public DefaultVisualStudioRazorParser(
|
||||
ForegroundDispatcher dispatcher,
|
||||
VisualStudioDocumentTracker documentTracker,
|
||||
RazorProjectEngineFactoryService projectEngineFactory,
|
||||
ProjectSnapshotProjectEngineFactory projectEngineFactory,
|
||||
ErrorReporter errorReporter,
|
||||
VisualStudioCompletionBroker completionBroker)
|
||||
{
|
||||
|
|
@ -167,8 +168,18 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
{
|
||||
_dispatcher.AssertForegroundThread();
|
||||
|
||||
// Make sure any tests use the real thing or a good mock. These tests can cause failures
|
||||
// that are hard to understand when this throws.
|
||||
Debug.Assert(_documentTracker.IsSupportedProject);
|
||||
Debug.Assert(_documentTracker.ProjectSnapshot != null);
|
||||
|
||||
_projectEngine = _projectEngineFactory.Create(_documentTracker.ProjectSnapshot, ConfigureProjectEngine);
|
||||
|
||||
Debug.Assert(_projectEngine != null);
|
||||
Debug.Assert(_projectEngine.Engine != null);
|
||||
Debug.Assert(_projectEngine.FileSystem != null);
|
||||
|
||||
var projectDirectory = Path.GetDirectoryName(_documentTracker.ProjectPath);
|
||||
_projectEngine = _projectEngineFactory.Create(projectDirectory, ConfigureProjectEngine);
|
||||
_parser = new BackgroundParser(_projectEngine, FilePath, projectDirectory);
|
||||
_parser.ResultsReady += OnResultsReady;
|
||||
_parser.Start();
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
internal class DefaultVisualStudioRazorParserFactory : VisualStudioRazorParserFactory
|
||||
{
|
||||
private readonly ForegroundDispatcher _dispatcher;
|
||||
private readonly RazorProjectEngineFactoryService _projectEngineFactoryService;
|
||||
private readonly ProjectSnapshotProjectEngineFactory _projectEngineFactory;
|
||||
private readonly VisualStudioCompletionBroker _completionBroker;
|
||||
private readonly ErrorReporter _errorReporter;
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
ForegroundDispatcher dispatcher,
|
||||
ErrorReporter errorReporter,
|
||||
VisualStudioCompletionBroker completionBroker,
|
||||
RazorProjectEngineFactoryService projectEngineFactoryService)
|
||||
ProjectSnapshotProjectEngineFactory projectEngineFactory)
|
||||
{
|
||||
if (dispatcher == null)
|
||||
{
|
||||
|
|
@ -34,15 +34,15 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
throw new ArgumentNullException(nameof(completionBroker));
|
||||
}
|
||||
|
||||
if (projectEngineFactoryService == null)
|
||||
if (projectEngineFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectEngineFactoryService));
|
||||
throw new ArgumentNullException(nameof(projectEngineFactory));
|
||||
}
|
||||
|
||||
_dispatcher = dispatcher;
|
||||
_errorReporter = errorReporter;
|
||||
_completionBroker = completionBroker;
|
||||
_projectEngineFactoryService = projectEngineFactoryService;
|
||||
_projectEngineFactory = projectEngineFactory;
|
||||
}
|
||||
|
||||
public override VisualStudioRazorParser Create(VisualStudioDocumentTracker documentTracker)
|
||||
|
|
@ -57,7 +57,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
var parser = new DefaultVisualStudioRazorParser(
|
||||
_dispatcher,
|
||||
documentTracker,
|
||||
_projectEngineFactoryService,
|
||||
_projectEngineFactory,
|
||||
_errorReporter,
|
||||
_completionBroker);
|
||||
return parser;
|
||||
|
|
|
|||
|
|
@ -35,13 +35,13 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
var workspaceServices = languageServices.WorkspaceServices;
|
||||
var errorReporter = workspaceServices.GetRequiredService<ErrorReporter>();
|
||||
var completionBroker = languageServices.GetRequiredService<VisualStudioCompletionBroker>();
|
||||
var projectEngineFactoryService = languageServices.GetRequiredService<RazorProjectEngineFactoryService>();
|
||||
var projectEngineFactory = workspaceServices.GetRequiredService<ProjectSnapshotProjectEngineFactory>();
|
||||
|
||||
return new DefaultVisualStudioRazorParserFactory(
|
||||
_foregroundDispatcher,
|
||||
errorReporter,
|
||||
completionBroker,
|
||||
projectEngineFactoryService);
|
||||
projectEngineFactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor.Editor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
using Microsoft.VisualStudio.Text.Editor;
|
||||
|
||||
|
|
@ -29,6 +30,8 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
|
||||
public abstract Project Project { get; }
|
||||
|
||||
internal abstract ProjectSnapshot ProjectSnapshot { get; }
|
||||
|
||||
public abstract Workspace Workspace { get; }
|
||||
|
||||
public abstract ITextBuffer TextBuffer { get; }
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
// 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.
|
||||
|
||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Composition;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
[Export(typeof(IRazorEngineDirectiveResolver))]
|
||||
internal class DefaultRazorEngineDirectiveResolver : IRazorEngineDirectiveResolver
|
||||
{
|
||||
public async Task<IEnumerable<DirectiveDescriptor>> GetRazorEngineDirectivesAsync(Workspace workspace, Project project, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
try
|
||||
{
|
||||
var client = await RazorLanguageServiceClientFactory.CreateAsync(workspace, cancellationToken);
|
||||
|
||||
using (var session = await client.CreateSessionAsync(project.Solution))
|
||||
{
|
||||
var directives = await session.InvokeAsync<IEnumerable<DirectiveDescriptor>>("GetDirectivesAsync", new object[] { project.Id.Id, "Foo", }, cancellationToken).ConfigureAwait(false);
|
||||
return directives;
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
Resources.FormatUnexpectedException(
|
||||
typeof(DefaultRazorEngineDirectiveResolver).FullName,
|
||||
nameof(GetRazorEngineDirectivesAsync)),
|
||||
exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
// 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.
|
||||
|
||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
using System;
|
||||
using System.Composition;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
[Export(typeof(IRazorEngineDocumentGenerator))]
|
||||
internal class DefaultRazorEngineDocumentGenerator : IRazorEngineDocumentGenerator
|
||||
{
|
||||
public async Task<RazorEngineDocument> GenerateDocumentAsync(Workspace workspace, Project project, string filePath, string text, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
try
|
||||
{
|
||||
var client = await RazorLanguageServiceClientFactory.CreateAsync(workspace, cancellationToken);
|
||||
|
||||
using (var session = await client.CreateSessionAsync(project.Solution))
|
||||
{
|
||||
var document = await session.InvokeAsync<RazorEngineDocument>("GenerateDocumentAsync", new object[] { project.Id.Id, "Foo", filePath, text }, cancellationToken).ConfigureAwait(false);
|
||||
return document;
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
Resources.FormatUnexpectedException(
|
||||
typeof(DefaultRazorEngineDocumentGenerator).FullName,
|
||||
nameof(GenerateDocumentAsync)),
|
||||
exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
// 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.
|
||||
|
||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
internal interface IRazorEngineDirectiveResolver
|
||||
{
|
||||
Task<IEnumerable<DirectiveDescriptor>> GetRazorEngineDirectivesAsync(Workspace workspace, Project project, CancellationToken cancellationToken = default(CancellationToken));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
// 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.
|
||||
|
||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
internal interface IRazorEngineDocumentGenerator
|
||||
{
|
||||
Task<RazorEngineDocument> GenerateDocumentAsync(Workspace workspace, Project project, string filePath, string text, CancellationToken cancellationToken = default(CancellationToken));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -43,7 +43,7 @@
|
|||
<Reference Include="System.Xaml"/>
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
<!--
|
||||
The ProjectSystem.SDK tasks that handle XamlPropertyRule don't work on the dotnet core version
|
||||
of MSBuild. The workaround here is to only hardcode the generated code location such that it gets
|
||||
checked in. Then we don't need to generate it at build time.
|
||||
|
|
@ -59,6 +59,9 @@
|
|||
<None Include="$(RulesDirectory)RazorGeneral.xaml">
|
||||
<Link>ProjectSystem\Rules\RazorGeneral.xaml</Link>
|
||||
</None>
|
||||
<None Include="$(RulesDirectory)RazorGenerateWithTargetPath.xaml">
|
||||
<Link>ProjectSystem\Rules\RazorGenerateWithTargetPath.xaml</Link>
|
||||
</None>
|
||||
<EmbeddedResource Include="$(RulesDirectory)RazorConfiguration.xaml">
|
||||
<LogicalName>XamlRuleToCode:RazorConfiguration.xaml</LogicalName>
|
||||
</EmbeddedResource>
|
||||
|
|
@ -68,6 +71,9 @@
|
|||
<EmbeddedResource Include="$(RulesDirectory)RazorGeneral.xaml">
|
||||
<LogicalName>XamlRuleToCode:RazorGeneral.xaml</LogicalName>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="$(RulesDirectory)RazorGenerateWithTargetPath.xaml">
|
||||
<LogicalName>XamlRuleToCode:RazorGenerateWithTargetPath.xaml</LogicalName>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(MSBuildRuntimeType)'!='Core'">
|
||||
<XamlPropertyRule Include="$(RulesDirectory)RazorConfiguration.xaml">
|
||||
|
|
@ -92,6 +98,13 @@
|
|||
<RuleInjectionClassName>RazorProjectProperties</RuleInjectionClassName>
|
||||
<OutputPath>ProjectSystem\Rules\</OutputPath>
|
||||
</XamlPropertyRule>
|
||||
<XamlPropertyRule Include="$(RulesDirectory)RazorGenerateWithTargetPath.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:GenerateRuleSourceFromXaml</Generator>
|
||||
<Namespace>Microsoft.CodeAnalysis.Razor.ProjectSystem.Rules</Namespace>
|
||||
<RuleInjectionClassName>RazorProjectProperties</RuleInjectionClassName>
|
||||
<OutputPath>ProjectSystem\Rules\</OutputPath>
|
||||
</XamlPropertyRule>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="ProjectSystem\Rules\RazorConfiguration.cs">
|
||||
|
|
@ -103,6 +116,9 @@
|
|||
<Compile Update="ProjectSystem\Rules\RazorGeneral.cs">
|
||||
<DependentUpon>ProjectSystem\Rules\RazorGeneral.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="ProjectSystem\Rules\RazorGenerateWithTargetPath.cs">
|
||||
<DependentUpon>ProjectSystem\Rules\RazorGenerateWithTargetPath.xaml</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
|
|
|
|||
|
|
@ -16,15 +16,15 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
internal class OOPTagHelperResolver : TagHelperResolver
|
||||
{
|
||||
private readonly DefaultTagHelperResolver _defaultResolver;
|
||||
private readonly RazorProjectEngineFactoryService _engineFactory;
|
||||
private readonly ProjectSnapshotProjectEngineFactory _factory;
|
||||
private readonly ErrorReporter _errorReporter;
|
||||
private readonly Workspace _workspace;
|
||||
|
||||
public OOPTagHelperResolver(RazorProjectEngineFactoryService engineFactory, ErrorReporter errorReporter, Workspace workspace)
|
||||
public OOPTagHelperResolver(ProjectSnapshotProjectEngineFactory factory, ErrorReporter errorReporter, Workspace workspace)
|
||||
{
|
||||
if (engineFactory == null)
|
||||
if (factory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(engineFactory));
|
||||
throw new ArgumentNullException(nameof(factory));
|
||||
}
|
||||
|
||||
if (errorReporter == null)
|
||||
|
|
@ -37,11 +37,11 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
throw new ArgumentNullException(nameof(workspace));
|
||||
}
|
||||
|
||||
_engineFactory = engineFactory;
|
||||
_factory = factory;
|
||||
_errorReporter = errorReporter;
|
||||
_workspace = workspace;
|
||||
|
||||
_defaultResolver = new DefaultTagHelperResolver(_engineFactory);
|
||||
_defaultResolver = new DefaultTagHelperResolver();
|
||||
}
|
||||
|
||||
public override async Task<TagHelperResolutionResult> GetTagHelpersAsync(ProjectSnapshot project, CancellationToken cancellationToken = default)
|
||||
|
|
@ -63,7 +63,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
// 3. Use fallback factory in process
|
||||
//
|
||||
// Calling into RazorTemplateEngineFactoryService.Create will accomplish #2 and #3 in one step.
|
||||
var factory = _engineFactory.FindSerializableFactory(project);
|
||||
var factory = _factory.FindSerializableFactory(project);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,21 +1,19 @@
|
|||
// 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.Composition;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Host.Mef;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
[Shared]
|
||||
[ExportLanguageServiceFactory(typeof(TagHelperResolver), RazorLanguage.Name, ServiceLayer.Host)]
|
||||
internal class OOPTagHelperResolverFactory : ILanguageServiceFactory
|
||||
{
|
||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
||||
{
|
||||
return new OOPTagHelperResolver(
|
||||
languageServices.GetRequiredService<RazorProjectEngineFactoryService>(),
|
||||
languageServices.WorkspaceServices.GetRequiredService<ProjectSnapshotProjectEngineFactory>(),
|
||||
languageServices.WorkspaceServices.GetRequiredService<ErrorReporter>(),
|
||||
languageServices.WorkspaceServices.Workspace);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -13,8 +14,7 @@ using Microsoft.AspNetCore.Razor.Language;
|
|||
using Microsoft.VisualStudio.LanguageServices;
|
||||
using Microsoft.VisualStudio.ProjectSystem;
|
||||
using Microsoft.VisualStudio.ProjectSystem.Properties;
|
||||
using ProjectState = System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IProjectRuleSnapshot>;
|
||||
using ProjectStateItem = System.Collections.Generic.KeyValuePair<string, System.Collections.Immutable.IImmutableDictionary<string, string>>;
|
||||
using Item = System.Collections.Generic.KeyValuePair<string, System.Collections.Immutable.IImmutableDictionary<string, string>>;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
|
|
@ -62,7 +62,13 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
receiver,
|
||||
initialDataAsNew: true,
|
||||
suppressVersionOnlyUpdates: true,
|
||||
ruleNames: new string[] { Rules.RazorGeneral.SchemaName, Rules.RazorConfiguration.SchemaName, Rules.RazorExtension.SchemaName });
|
||||
ruleNames: new string[]
|
||||
{
|
||||
Rules.RazorGeneral.SchemaName,
|
||||
Rules.RazorConfiguration.SchemaName,
|
||||
Rules.RazorExtension.SchemaName,
|
||||
Rules.RazorGenerateWithTargetPath.SchemaName,
|
||||
});
|
||||
}
|
||||
|
||||
protected override async Task DisposeCoreAsync(bool initialized)
|
||||
|
|
@ -90,12 +96,36 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
if (TryGetConfiguration(update.Value.CurrentState, out var configuration))
|
||||
{
|
||||
var hostProject = new HostProject(CommonServices.UnconfiguredProject.FullPath, configuration);
|
||||
await UpdateProjectUnsafeAsync(hostProject).ConfigureAwait(false);
|
||||
|
||||
// We need to deal with the case where the project was uninitialized, but now
|
||||
// is valid for Razor. In that case we might have previously seen all of the documents
|
||||
// but ignored them because the project wasn't active.
|
||||
//
|
||||
// So what we do to deal with this, is that we 'remove' all changed and removed items
|
||||
// and then we 'add' all current items. This allows minimal churn to the PSM, but still
|
||||
// makes us up to date.
|
||||
var documents = GetCurrentDocuments(update.Value);
|
||||
var changedDocuments = GetChangedAndRemovedDocuments(update.Value);
|
||||
|
||||
await UpdateAsync(() =>
|
||||
{
|
||||
UpdateProjectUnsafe(hostProject);
|
||||
|
||||
for (var i = 0; i < changedDocuments.Length; i++)
|
||||
{
|
||||
RemoveDocumentUnsafe(changedDocuments[i]);
|
||||
}
|
||||
|
||||
for (var i = 0; i < documents.Length; i++)
|
||||
{
|
||||
AddDocumentUnsafe(documents[i]);
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ok we can't find a configuration. Let's assume this project isn't using Razor then.
|
||||
await UpdateProjectUnsafeAsync(null).ConfigureAwait(false);
|
||||
await UpdateAsync(UninitializeProjectUnsafe).ConfigureAwait(false);
|
||||
}
|
||||
});
|
||||
}, registerFaultHandler: true);
|
||||
|
|
@ -103,34 +133,34 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
// Internal for testing
|
||||
internal static bool TryGetConfiguration(
|
||||
ProjectState projectState,
|
||||
IImmutableDictionary<string, IProjectRuleSnapshot> state,
|
||||
out RazorConfiguration configuration)
|
||||
{
|
||||
if (!TryGetDefaultConfiguration(projectState, out var defaultConfiguration))
|
||||
if (!TryGetDefaultConfiguration(state, out var defaultConfiguration))
|
||||
{
|
||||
configuration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryGetLanguageVersion(projectState, out var languageVersion))
|
||||
if (!TryGetLanguageVersion(state, out var languageVersion))
|
||||
{
|
||||
configuration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryGetConfigurationItem(defaultConfiguration, projectState, out var configurationItem))
|
||||
if (!TryGetConfigurationItem(defaultConfiguration, state, out var configurationItem))
|
||||
{
|
||||
configuration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryGetConfiguredExtensionNames(configurationItem, out var configuredExtensionNames))
|
||||
if (!TryGetExtensionNames(configurationItem, out var extensionNames))
|
||||
{
|
||||
configuration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryGetExtensions(configuredExtensionNames, projectState, out var extensions))
|
||||
if (!TryGetExtensions(extensionNames, state, out var extensions))
|
||||
{
|
||||
configuration = null;
|
||||
return false;
|
||||
|
|
@ -142,9 +172,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
|
||||
// Internal for testing
|
||||
internal static bool TryGetDefaultConfiguration(ProjectState projectState, out string defaultConfiguration)
|
||||
internal static bool TryGetDefaultConfiguration(
|
||||
IImmutableDictionary<string, IProjectRuleSnapshot> state,
|
||||
out string defaultConfiguration)
|
||||
{
|
||||
if (!projectState.TryGetValue(Rules.RazorGeneral.SchemaName, out var rule))
|
||||
if (!state.TryGetValue(Rules.RazorGeneral.SchemaName, out var rule))
|
||||
{
|
||||
defaultConfiguration = null;
|
||||
return false;
|
||||
|
|
@ -166,9 +198,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static bool TryGetLanguageVersion(ProjectState projectState, out RazorLanguageVersion languageVersion)
|
||||
internal static bool TryGetLanguageVersion(
|
||||
IImmutableDictionary<string, IProjectRuleSnapshot> state,
|
||||
out RazorLanguageVersion languageVersion)
|
||||
{
|
||||
if (!projectState.TryGetValue(Rules.RazorGeneral.SchemaName, out var rule))
|
||||
if (!state.TryGetValue(Rules.RazorGeneral.SchemaName, out var rule))
|
||||
{
|
||||
languageVersion = null;
|
||||
return false;
|
||||
|
|
@ -197,17 +231,17 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// Internal for testing
|
||||
internal static bool TryGetConfigurationItem(
|
||||
string configuration,
|
||||
ProjectState projectState,
|
||||
out ProjectStateItem configurationItem)
|
||||
IImmutableDictionary<string, IProjectRuleSnapshot> state,
|
||||
out Item configurationItem)
|
||||
{
|
||||
if (!projectState.TryGetValue(Rules.RazorConfiguration.PrimaryDataSourceItemType, out var configurationState))
|
||||
if (!state.TryGetValue(Rules.RazorConfiguration.PrimaryDataSourceItemType, out var configurationState))
|
||||
{
|
||||
configurationItem = default(ProjectStateItem);
|
||||
configurationItem = default(Item);
|
||||
return false;
|
||||
}
|
||||
|
||||
var razorConfigurationItems = configurationState.Items;
|
||||
foreach (var item in razorConfigurationItems)
|
||||
var items = configurationState.Items;
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (item.Key == configuration)
|
||||
{
|
||||
|
|
@ -216,44 +250,49 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
}
|
||||
}
|
||||
|
||||
configurationItem = default(ProjectStateItem);
|
||||
configurationItem = default(Item);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static bool TryGetConfiguredExtensionNames(ProjectStateItem configurationItem, out string[] configuredExtensionNames)
|
||||
internal static bool TryGetExtensionNames(
|
||||
Item configurationItem,
|
||||
out string[] configuredExtensionNames)
|
||||
{
|
||||
if (!configurationItem.Value.TryGetValue(Rules.RazorConfiguration.ExtensionsProperty, out var extensionNamesValue))
|
||||
if (!configurationItem.Value.TryGetValue(Rules.RazorConfiguration.ExtensionsProperty, out var extensionNames))
|
||||
{
|
||||
configuredExtensionNames = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(extensionNamesValue))
|
||||
if (string.IsNullOrEmpty(extensionNames))
|
||||
{
|
||||
configuredExtensionNames = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
configuredExtensionNames = extensionNamesValue.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
configuredExtensionNames = extensionNames.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static bool TryGetExtensions(string[] configuredExtensionNames, ProjectState projectState, out ProjectSystemRazorExtension[] extensions)
|
||||
internal static bool TryGetExtensions(
|
||||
string[] extensionNames,
|
||||
IImmutableDictionary<string, IProjectRuleSnapshot> state,
|
||||
out ProjectSystemRazorExtension[] extensions)
|
||||
{
|
||||
if (!projectState.TryGetValue(Rules.RazorExtension.PrimaryDataSourceItemType, out var extensionState))
|
||||
if (!state.TryGetValue(Rules.RazorExtension.PrimaryDataSourceItemType, out var rule))
|
||||
{
|
||||
extensions = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var extensionItems = extensionState.Items;
|
||||
var items = rule.Items;
|
||||
var extensionList = new List<ProjectSystemRazorExtension>();
|
||||
foreach (var item in extensionItems)
|
||||
foreach (var item in items)
|
||||
{
|
||||
var extensionName = item.Key;
|
||||
if (configuredExtensionNames.Contains(extensionName))
|
||||
if (extensionNames.Contains(extensionName))
|
||||
{
|
||||
extensionList.Add(new ProjectSystemRazorExtension(extensionName));
|
||||
}
|
||||
|
|
@ -262,5 +301,53 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
extensions = extensionList.ToArray();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private HostDocument[] GetCurrentDocuments(IProjectSubscriptionUpdate update)
|
||||
{
|
||||
if (!update.CurrentState.TryGetValue(Rules.RazorGenerateWithTargetPath.SchemaName, out var rule))
|
||||
{
|
||||
return Array.Empty<HostDocument>();
|
||||
}
|
||||
|
||||
var documents = new List<HostDocument>();
|
||||
foreach (var kvp in rule.Items)
|
||||
{
|
||||
if (kvp.Value.TryGetValue(Rules.RazorGenerateWithTargetPath.TargetPathProperty, out var targetPath) &&
|
||||
!string.IsNullOrWhiteSpace(kvp.Key) &&
|
||||
!string.IsNullOrWhiteSpace(targetPath))
|
||||
{
|
||||
var filePath = CommonServices.UnconfiguredProject.MakeRooted(kvp.Key);
|
||||
documents.Add(new HostDocument(filePath, targetPath));
|
||||
}
|
||||
}
|
||||
|
||||
return documents.ToArray();
|
||||
}
|
||||
|
||||
private HostDocument[] GetChangedAndRemovedDocuments(IProjectSubscriptionUpdate update)
|
||||
{
|
||||
if (!update.ProjectChanges.TryGetValue(Rules.RazorGenerateWithTargetPath.SchemaName, out var rule))
|
||||
{
|
||||
return Array.Empty<HostDocument>();
|
||||
}
|
||||
|
||||
var documents = new List<HostDocument>();
|
||||
foreach (var key in rule.Difference.RemovedItems.Concat(rule.Difference.ChangedItems))
|
||||
{
|
||||
if (rule.Before.Items.TryGetValue(key, out var value))
|
||||
{
|
||||
if (value.TryGetValue(Rules.RazorGenerateWithTargetPath.TargetPathProperty, out var targetPath) &&
|
||||
!string.IsNullOrWhiteSpace(key) &&
|
||||
!string.IsNullOrWhiteSpace(targetPath))
|
||||
{
|
||||
var filePath = CommonServices.UnconfiguredProject.MakeRooted(key);
|
||||
documents.Add(new HostDocument(filePath, targetPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return documents.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -85,20 +85,23 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
await ExecuteWithLock(async () =>
|
||||
{
|
||||
string mvcReferenceFullPath = null;
|
||||
var references = update.Value.CurrentState[ResolvedCompilationReference.SchemaName].Items;
|
||||
foreach (var reference in references)
|
||||
if (update.Value.CurrentState.ContainsKey(ResolvedCompilationReference.SchemaName))
|
||||
{
|
||||
if (reference.Key.EndsWith(MvcAssemblyFileName, StringComparison.OrdinalIgnoreCase))
|
||||
var references = update.Value.CurrentState[ResolvedCompilationReference.SchemaName].Items;
|
||||
foreach (var reference in references)
|
||||
{
|
||||
mvcReferenceFullPath = reference.Key;
|
||||
break;
|
||||
if (reference.Key.EndsWith(MvcAssemblyFileName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
mvcReferenceFullPath = reference.Key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mvcReferenceFullPath == null)
|
||||
{
|
||||
// Ok we can't find an MVC version. Let's assume this project isn't using Razor then.
|
||||
await UpdateProjectUnsafeAsync(null).ConfigureAwait(false);
|
||||
await UpdateAsync(UninitializeProjectUnsafe).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -106,13 +109,16 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
if (version == null)
|
||||
{
|
||||
// Ok we can't find an MVC version. Let's assume this project isn't using Razor then.
|
||||
await UpdateProjectUnsafeAsync(null).ConfigureAwait(false);
|
||||
await UpdateAsync(UninitializeProjectUnsafe).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var configuration = FallbackRazorConfiguration.SelectConfiguration(version);
|
||||
var hostProject = new HostProject(CommonServices.UnconfiguredProject.FullPath, configuration);
|
||||
await UpdateProjectUnsafeAsync(hostProject).ConfigureAwait(false);
|
||||
await UpdateAsync(() =>
|
||||
{
|
||||
UpdateProjectUnsafe(hostProject);
|
||||
}).ConfigureAwait(false);
|
||||
});
|
||||
}, registerFaultHandler: true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@
|
|||
// 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.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.LanguageServices;
|
||||
|
|
@ -18,6 +21,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
private ProjectSnapshotManagerBase _projectManager;
|
||||
private HostProject _current;
|
||||
private Dictionary<string, HostDocument> _currentDocuments;
|
||||
|
||||
public RazorProjectHostBase(
|
||||
IUnconfiguredProjectCommonServices commonServices,
|
||||
|
|
@ -38,6 +42,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
_workspace = workspace;
|
||||
|
||||
_lock = new AsyncSemaphore(initialCount: 1);
|
||||
_currentDocuments = new Dictionary<string, HostDocument>(FilePathComparer.Instance);
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
|
|
@ -67,8 +72,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
_projectManager = projectManager;
|
||||
|
||||
_lock = new AsyncSemaphore(initialCount: 1);
|
||||
_currentDocuments = new Dictionary<string, HostDocument>(FilePathComparer.Instance);
|
||||
}
|
||||
|
||||
protected HostProject Current => _current;
|
||||
|
||||
protected IUnconfiguredProjectCommonServices CommonServices { get; }
|
||||
|
||||
// internal for tests. The product will call through the IProjectDynamicLoadComponent interface.
|
||||
|
|
@ -94,7 +102,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
if (_current != null)
|
||||
{
|
||||
await UpdateProjectUnsafeAsync(null).ConfigureAwait(false);
|
||||
await UpdateAsync(UninitializeProjectUnsafe).ConfigureAwait(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -113,10 +121,21 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
if (_current != null)
|
||||
{
|
||||
var old = _current;
|
||||
await UpdateProjectUnsafeAsync(null).ConfigureAwait(false);
|
||||
var oldDocuments = _currentDocuments.Values.ToArray();
|
||||
|
||||
var filePath = CommonServices.UnconfiguredProject.FullPath;
|
||||
await UpdateProjectUnsafeAsync(new HostProject(filePath, old.Configuration)).ConfigureAwait(false);
|
||||
await UpdateAsync(UninitializeProjectUnsafe).ConfigureAwait(false);
|
||||
|
||||
await UpdateAsync(() =>
|
||||
{
|
||||
var filePath = CommonServices.UnconfiguredProject.FullPath;
|
||||
UpdateProjectUnsafe(new HostProject(filePath, old.Configuration));
|
||||
|
||||
// This should no-op in the common case, just putting it here for insurance.
|
||||
for (var i = 0; i < oldDocuments.Length; i++)
|
||||
{
|
||||
AddDocumentUnsafe(oldDocuments[i]);
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -134,12 +153,21 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
return _projectManager;
|
||||
}
|
||||
|
||||
// Must be called inside the lock.
|
||||
protected async Task UpdateProjectUnsafeAsync(HostProject project)
|
||||
protected async Task UpdateAsync(Action action)
|
||||
{
|
||||
await CommonServices.ThreadingService.SwitchToUIThread();
|
||||
var projectManager = GetProjectManager();
|
||||
action();
|
||||
}
|
||||
|
||||
protected void UninitializeProjectUnsafe()
|
||||
{
|
||||
ClearDocumentsUnsafe();
|
||||
UpdateProjectUnsafe(null);
|
||||
}
|
||||
|
||||
protected void UpdateProjectUnsafe(HostProject project)
|
||||
{
|
||||
var projectManager = GetProjectManager();
|
||||
if (_current == null && project == null)
|
||||
{
|
||||
// This is a no-op. This project isn't using Razor.
|
||||
|
|
@ -150,6 +178,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
}
|
||||
else if (_current != null && project == null)
|
||||
{
|
||||
Debug.Assert(_currentDocuments.Count == 0);
|
||||
projectManager.HostProjectRemoved(_current);
|
||||
}
|
||||
else
|
||||
|
|
@ -159,6 +188,40 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
_current = project;
|
||||
}
|
||||
|
||||
protected void AddDocumentUnsafe(HostDocument document)
|
||||
{
|
||||
var projectManager = GetProjectManager();
|
||||
|
||||
if (_currentDocuments.ContainsKey(document.FilePath))
|
||||
{
|
||||
// Ignore duplicates
|
||||
return;
|
||||
}
|
||||
|
||||
projectManager.DocumentAdded(_current, document);
|
||||
_currentDocuments.Add(document.FilePath, document);
|
||||
}
|
||||
|
||||
protected void RemoveDocumentUnsafe(HostDocument document)
|
||||
{
|
||||
var projectManager = GetProjectManager();
|
||||
|
||||
projectManager.DocumentRemoved(_current, document);
|
||||
_currentDocuments.Remove(document.FilePath);
|
||||
}
|
||||
|
||||
protected void ClearDocumentsUnsafe()
|
||||
{
|
||||
var projectManager = GetProjectManager();
|
||||
|
||||
foreach (var kvp in _currentDocuments)
|
||||
{
|
||||
_projectManager.DocumentRemoved(_current, kvp.Value);
|
||||
}
|
||||
|
||||
_currentDocuments.Clear();
|
||||
}
|
||||
|
||||
protected async Task ExecuteWithLock(Func<Task> func)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Rule
|
||||
Description="Configuration Properties"
|
||||
DisplayName="Configuration Properties"
|
||||
Name="RazorConfiguration"
|
||||
PageTemplate="generic"
|
||||
xmlns="http://schemas.microsoft.com/build/2009/properties">
|
||||
<Rule.DataSource>
|
||||
<DataSource
|
||||
Persistence="ProjectFile"
|
||||
HasConfigurationCondition="True"
|
||||
ItemType="RazorConfiguration" />
|
||||
</Rule.DataSource>
|
||||
|
||||
<Rule.Categories>
|
||||
<Category
|
||||
Name="General"
|
||||
DisplayName="General" />
|
||||
</Rule.Categories>
|
||||
|
||||
<StringProperty
|
||||
Category="General"
|
||||
Description="Razor Extensions"
|
||||
DisplayName="Razor Extensions"
|
||||
Name="Extensions"
|
||||
ReadOnly="True"
|
||||
Visible="True" />
|
||||
|
||||
</Rule>
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Rule
|
||||
Description="Extension Properties"
|
||||
DisplayName="Extension Properties"
|
||||
Name="RazorExtension"
|
||||
PageTemplate="generic"
|
||||
xmlns="http://schemas.microsoft.com/build/2009/properties">
|
||||
<Rule.DataSource>
|
||||
<DataSource
|
||||
Persistence="ProjectFile"
|
||||
HasConfigurationCondition="True"
|
||||
ItemType="RazorExtension" />
|
||||
</Rule.DataSource>
|
||||
|
||||
<Rule.Categories>
|
||||
<Category
|
||||
Name="General"
|
||||
DisplayName="General" />
|
||||
</Rule.Categories>
|
||||
|
||||
<StringProperty
|
||||
Category="General"
|
||||
Description="Razor Extension Assembly Name"
|
||||
DisplayName="Razor Extension Assembly Name"
|
||||
Name="AssemblyName"
|
||||
ReadOnly="True"
|
||||
Visible="True" />
|
||||
|
||||
<StringProperty
|
||||
Category="General"
|
||||
Description="Razor Extension Assembly File Path"
|
||||
DisplayName="Razor Extension Assembly File Path"
|
||||
Name="AssemblyFilePath"
|
||||
ReadOnly="True"
|
||||
Visible="True" />
|
||||
|
||||
</Rule>
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Rule
|
||||
Description="Razor Properties"
|
||||
DisplayName="Razor Properties"
|
||||
Name="RazorGeneral"
|
||||
PageTemplate="generic"
|
||||
xmlns="http://schemas.microsoft.com/build/2009/properties">
|
||||
<Rule.DataSource>
|
||||
<DataSource
|
||||
Persistence="ProjectFile"
|
||||
HasConfigurationCondition="True" />
|
||||
</Rule.DataSource>
|
||||
|
||||
<Rule.Categories>
|
||||
<Category
|
||||
Name="General"
|
||||
DisplayName="General" />
|
||||
</Rule.Categories>
|
||||
|
||||
<StringProperty
|
||||
Category="General"
|
||||
Description="Razor Language Version"
|
||||
DisplayName="Razor Language Version"
|
||||
Name="RazorLangVersion"
|
||||
ReadOnly="True"
|
||||
Visible="True" />
|
||||
|
||||
<StringProperty
|
||||
Category="General"
|
||||
Description="Razor Configuration Name"
|
||||
DisplayName="Razor Configuration Name"
|
||||
Name="RazorDefaultConfiguration"
|
||||
ReadOnly="True"
|
||||
Visible="True" />
|
||||
|
||||
</Rule>
|
||||
|
|
@ -0,0 +1,212 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem.Rules {
|
||||
|
||||
|
||||
internal partial class RazorGenerateWithTargetPath {
|
||||
|
||||
/// <summary>Backing field for deserialized rule.<see cref='Microsoft.Build.Framework.XamlTypes.Rule'/>.</summary>
|
||||
private static Microsoft.Build.Framework.XamlTypes.Rule deserializedFallbackRule;
|
||||
|
||||
/// <summary>The name of the schema to look for at runtime to fulfill property access.</summary>
|
||||
internal const string SchemaName = "RazorGenerateWithTargetPath";
|
||||
|
||||
/// <summary>The ItemType given in the Rule.DataSource property. May not apply to every Property's individual DataSource.</summary>
|
||||
internal const string PrimaryDataSourceItemType = "RazorGenerateWithTargetPath";
|
||||
|
||||
/// <summary>The Label given in the Rule.DataSource property. May not apply to every Property's individual DataSource.</summary>
|
||||
internal const string PrimaryDataSourceLabel = "";
|
||||
|
||||
/// <summary>Target Path (The "TargetPath" property).</summary>
|
||||
internal const string TargetPathProperty = "TargetPath";
|
||||
|
||||
/// <summary>Backing field for the <see cref='Microsoft.Build.Framework.XamlTypes.Rule'/> property.</summary>
|
||||
private Microsoft.VisualStudio.ProjectSystem.Properties.IRule rule;
|
||||
|
||||
/// <summary>Backing field for the file name of the rule property.</summary>
|
||||
private string file;
|
||||
|
||||
/// <summary>Backing field for the ItemType property.</summary>
|
||||
private string itemType;
|
||||
|
||||
/// <summary>Backing field for the ItemName property.</summary>
|
||||
private string itemName;
|
||||
|
||||
/// <summary>Configured Project</summary>
|
||||
private Microsoft.VisualStudio.ProjectSystem.ConfiguredProject configuredProject;
|
||||
|
||||
/// <summary>The dictionary of named catalogs.</summary>
|
||||
private System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog> catalogs;
|
||||
|
||||
/// <summary>Backing field for the <see cref='Microsoft.VisualStudio.ProjectSystem.Properties.IRule'/> property.</summary>
|
||||
private Microsoft.VisualStudio.ProjectSystem.Properties.IRule fallbackRule;
|
||||
|
||||
/// <summary>Thread locking object</summary>
|
||||
private object locker = new object();
|
||||
|
||||
/// <summary>Initializes a new instance of the RazorGenerateWithTargetPath class.</summary>
|
||||
internal RazorGenerateWithTargetPath(Microsoft.VisualStudio.ProjectSystem.Properties.IRule rule) {
|
||||
this.rule = rule;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the RazorGenerateWithTargetPath class.</summary>
|
||||
internal RazorGenerateWithTargetPath(Microsoft.VisualStudio.ProjectSystem.ConfiguredProject configuredProject, System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog> catalogs, string context, string file, string itemType, string itemName) :
|
||||
this(GetRule(System.Collections.Immutable.ImmutableDictionary.GetValueOrDefault(catalogs, context), file, itemType, itemName)) {
|
||||
if ((configuredProject == null)) {
|
||||
throw new System.ArgumentNullException("configuredProject");
|
||||
}
|
||||
this.configuredProject = configuredProject;
|
||||
this.catalogs = catalogs;
|
||||
this.file = file;
|
||||
this.itemType = itemType;
|
||||
this.itemName = itemName;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the RazorGenerateWithTargetPath class.</summary>
|
||||
internal RazorGenerateWithTargetPath(Microsoft.VisualStudio.ProjectSystem.Properties.IRule rule, Microsoft.VisualStudio.ProjectSystem.ConfiguredProject configuredProject) :
|
||||
this(rule) {
|
||||
if ((rule == null)) {
|
||||
throw new System.ArgumentNullException("rule");
|
||||
}
|
||||
if ((configuredProject == null)) {
|
||||
throw new System.ArgumentNullException("configuredProject");
|
||||
}
|
||||
this.configuredProject = configuredProject;
|
||||
this.rule = rule;
|
||||
this.file = this.rule.File;
|
||||
this.itemType = this.rule.ItemType;
|
||||
this.itemName = this.rule.ItemName;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the RazorGenerateWithTargetPath class.</summary>
|
||||
internal RazorGenerateWithTargetPath(Microsoft.VisualStudio.ProjectSystem.ConfiguredProject configuredProject, System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog> catalogs, string context, Microsoft.VisualStudio.ProjectSystem.Properties.IProjectPropertiesContext propertyContext) :
|
||||
this(configuredProject, catalogs, context, GetContextFile(propertyContext), propertyContext.ItemType, propertyContext.ItemName) {
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the RazorGenerateWithTargetPath class that assumes a project context (neither property sheet nor items).</summary>
|
||||
internal RazorGenerateWithTargetPath(Microsoft.VisualStudio.ProjectSystem.ConfiguredProject configuredProject, System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog> catalogs) :
|
||||
this(configuredProject, catalogs, "Project", null, null, null) {
|
||||
}
|
||||
|
||||
/// <summary>Gets the IRule used to get and set properties.</summary>
|
||||
public Microsoft.VisualStudio.ProjectSystem.Properties.IRule Rule {
|
||||
get {
|
||||
return this.rule;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Target Path</summary>
|
||||
internal Microsoft.VisualStudio.ProjectSystem.Properties.IEvaluatedProperty TargetPath {
|
||||
get {
|
||||
Microsoft.VisualStudio.ProjectSystem.Properties.IRule localRule = this.rule;
|
||||
if ((localRule == null)) {
|
||||
localRule = this.GeneratedFallbackRule;
|
||||
}
|
||||
if ((localRule == null)) {
|
||||
return null;
|
||||
}
|
||||
Microsoft.VisualStudio.ProjectSystem.Properties.IEvaluatedProperty property = ((Microsoft.VisualStudio.ProjectSystem.Properties.IEvaluatedProperty)(localRule.GetProperty(TargetPathProperty)));
|
||||
if (((property == null)
|
||||
&& (this.GeneratedFallbackRule != null))) {
|
||||
localRule = this.GeneratedFallbackRule;
|
||||
property = ((Microsoft.VisualStudio.ProjectSystem.Properties.IEvaluatedProperty)(localRule.GetProperty(TargetPathProperty)));
|
||||
}
|
||||
return property;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Get the fallback rule if the current rule on disk is missing or a property in the rule on disk is missing</summary>
|
||||
private Microsoft.VisualStudio.ProjectSystem.Properties.IRule GeneratedFallbackRule {
|
||||
get {
|
||||
if (((this.fallbackRule == null)
|
||||
&& (this.configuredProject != null))) {
|
||||
System.Threading.Monitor.Enter(this.locker);
|
||||
try {
|
||||
if ((this.fallbackRule == null)) {
|
||||
this.InitializeFallbackRule();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
System.Threading.Monitor.Exit(this.locker);
|
||||
}
|
||||
}
|
||||
return this.fallbackRule;
|
||||
}
|
||||
}
|
||||
|
||||
private static Microsoft.VisualStudio.ProjectSystem.Properties.IRule GetRule(Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog catalog, string file, string itemType, string itemName) {
|
||||
if ((catalog == null)) {
|
||||
return null;
|
||||
}
|
||||
return catalog.BindToContext(SchemaName, file, itemType, itemName);
|
||||
}
|
||||
|
||||
private static string GetContextFile(Microsoft.VisualStudio.ProjectSystem.Properties.IProjectPropertiesContext propertiesContext) {
|
||||
if ((propertiesContext.IsProjectFile == true)) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return propertiesContext.File;
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeFallbackRule() {
|
||||
if ((this.configuredProject == null)) {
|
||||
return;
|
||||
}
|
||||
Microsoft.Build.Framework.XamlTypes.Rule unboundRule = RazorGenerateWithTargetPath.deserializedFallbackRule;
|
||||
if ((unboundRule == null)) {
|
||||
System.IO.Stream xamlStream = null;
|
||||
System.Reflection.Assembly thisAssembly = System.Reflection.Assembly.GetExecutingAssembly();
|
||||
try {
|
||||
xamlStream = thisAssembly.GetManifestResourceStream("XamlRuleToCode:RazorGenerateWithTargetPath.xaml");
|
||||
Microsoft.Build.Framework.XamlTypes.IProjectSchemaNode root = ((Microsoft.Build.Framework.XamlTypes.IProjectSchemaNode)(System.Xaml.XamlServices.Load(xamlStream)));
|
||||
System.Collections.Generic.IEnumerator<System.Object> ruleEnumerator = root.GetSchemaObjects(typeof(Microsoft.Build.Framework.XamlTypes.Rule)).GetEnumerator();
|
||||
for (
|
||||
; ((unboundRule == null)
|
||||
&& ruleEnumerator.MoveNext());
|
||||
) {
|
||||
Microsoft.Build.Framework.XamlTypes.Rule t = ((Microsoft.Build.Framework.XamlTypes.Rule)(ruleEnumerator.Current));
|
||||
if (System.StringComparer.OrdinalIgnoreCase.Equals(t.Name, SchemaName)) {
|
||||
unboundRule = t;
|
||||
unboundRule.Name = "4d01f23e-96db-4c90-9c01-17b7f8b61243";
|
||||
RazorGenerateWithTargetPath.deserializedFallbackRule = unboundRule;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if ((xamlStream != null)) {
|
||||
((System.IDisposable)(xamlStream)).Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
this.configuredProject.Services.AdditionalRuleDefinitions.AddRuleDefinition(unboundRule, "FallbackRuleCodeGenerationContext");
|
||||
Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog catalog = this.configuredProject.Services.PropertyPagesCatalog.GetMemoryOnlyCatalog("FallbackRuleCodeGenerationContext");
|
||||
this.fallbackRule = catalog.BindToContext(unboundRule.Name, this.file, this.itemType, this.itemName);
|
||||
}
|
||||
}
|
||||
|
||||
internal partial class RazorProjectProperties {
|
||||
|
||||
private static System.Func<System.Threading.Tasks.Task<System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog>>, object, RazorGenerateWithTargetPath> CreateRazorGenerateWithTargetPathPropertiesDelegate = new System.Func<System.Threading.Tasks.Task<System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog>>, object, RazorGenerateWithTargetPath>(CreateRazorGenerateWithTargetPathProperties);
|
||||
|
||||
private static RazorGenerateWithTargetPath CreateRazorGenerateWithTargetPathProperties(System.Threading.Tasks.Task<System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog>> namedCatalogs, object state) {
|
||||
RazorProjectProperties that = ((RazorProjectProperties)(state));
|
||||
return new RazorGenerateWithTargetPath(that.ConfiguredProject, namedCatalogs.Result, "Project", that.File, that.ItemType, that.ItemName);
|
||||
}
|
||||
|
||||
/// <summary>Gets the strongly-typed property accessor used to get and set Razor Document Properties properties.</summary>
|
||||
internal System.Threading.Tasks.Task<RazorGenerateWithTargetPath> GetRazorGenerateWithTargetPathPropertiesAsync() {
|
||||
System.Threading.Tasks.Task<System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog>> namedCatalogsTask = this.GetNamedCatalogsAsync();
|
||||
return namedCatalogsTask.ContinueWith(CreateRazorGenerateWithTargetPathPropertiesDelegate, this, System.Threading.CancellationToken.None, System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously, System.Threading.Tasks.TaskScheduler.Default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
// 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.
|
||||
|
||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
internal class RazorEngineDocument
|
||||
{
|
||||
public string Text { get; set; }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -86,14 +86,15 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
public int UpdateProjectCfg_Done(IVsHierarchy pHierProj, IVsCfg pCfgProj, IVsCfg pCfgSln, uint dwAction, int fSuccess, int fCancel)
|
||||
{
|
||||
var projectPath = _projectService.GetProjectPath(pHierProj);
|
||||
|
||||
// Get the corresponding roslyn project by matching the project name and the project path.
|
||||
foreach (var projectSnapshot in _projectManager.Projects)
|
||||
var project = _projectManager.GetLoadedProject(projectPath);
|
||||
if (project != null && project.WorkspaceProject != null)
|
||||
{
|
||||
if (string.Equals(projectPath, projectSnapshot.FilePath, StringComparison.OrdinalIgnoreCase))
|
||||
var workspaceProject = _projectManager.Workspace.CurrentSolution.GetProject(project.WorkspaceProject.Id);
|
||||
if (workspaceProject != null)
|
||||
{
|
||||
_projectManager.HostProjectBuildComplete(projectSnapshot.HostProject);
|
||||
break;
|
||||
// Trigger a tag helper update by forcing the project manager to see the workspace Project
|
||||
// from the current solution.
|
||||
_projectManager.WorkspaceProjectChanged(workspaceProject);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -97,14 +97,15 @@ namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor
|
|||
}
|
||||
|
||||
var projectPath = _projectService.GetProjectPath(projectItem);
|
||||
|
||||
// Get the corresponding roslyn project by matching the project name and the project path.
|
||||
foreach (var projectSnapshot in _projectManager.Projects)
|
||||
var project = _projectManager.GetLoadedProject(projectPath);
|
||||
if (project != null && project.WorkspaceProject != null)
|
||||
{
|
||||
if (string.Equals(projectPath, projectSnapshot.FilePath, StringComparison.OrdinalIgnoreCase))
|
||||
var workspaceProject = _projectManager.Workspace.CurrentSolution.GetProject(project.WorkspaceProject.Id);
|
||||
if (workspaceProject != null)
|
||||
{
|
||||
_projectManager.HostProjectBuildComplete(projectSnapshot.HostProject);
|
||||
break;
|
||||
// Trigger a tag helper update by forcing the project manager to see the workspace Project
|
||||
// from the current solution.
|
||||
_projectManager.WorkspaceProjectChanged(workspaceProject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,12 +8,12 @@ using Microsoft.CodeAnalysis.Razor;
|
|||
|
||||
namespace Microsoft.CodeAnalysis.Host
|
||||
{
|
||||
internal class TestRazorLanguageServices : HostLanguageServices
|
||||
internal class TestLanguageServices : HostLanguageServices
|
||||
{
|
||||
private readonly HostWorkspaceServices _workspaceServices;
|
||||
private readonly IEnumerable<ILanguageService> _languageServices;
|
||||
|
||||
public TestRazorLanguageServices(HostWorkspaceServices workspaceServices, IEnumerable<ILanguageService> languageServices)
|
||||
public TestLanguageServices(HostWorkspaceServices workspaceServices, IEnumerable<ILanguageService> languageServices)
|
||||
{
|
||||
if (workspaceServices == null)
|
||||
{
|
||||
|
|
@ -13,14 +13,14 @@ namespace Microsoft.CodeAnalysis.Host
|
|||
private static readonly Workspace DefaultWorkspace = TestWorkspace.Create();
|
||||
|
||||
private readonly HostServices _hostServices;
|
||||
private readonly HostLanguageServices _razorLanguageServices;
|
||||
private readonly IEnumerable<IWorkspaceService> _workspaceServices;
|
||||
private readonly TestRazorLanguageServices _razorLanguageServices;
|
||||
private readonly Workspace _workspace;
|
||||
|
||||
public TestWorkspaceServices(
|
||||
HostServices hostServices,
|
||||
IEnumerable<IWorkspaceService> workspaceServices,
|
||||
IEnumerable<ILanguageService> razorLanguageServices,
|
||||
IEnumerable<ILanguageService> languageServices,
|
||||
Workspace workspace)
|
||||
{
|
||||
if (hostServices == null)
|
||||
|
|
@ -33,9 +33,9 @@ namespace Microsoft.CodeAnalysis.Host
|
|||
throw new ArgumentNullException(nameof(workspaceServices));
|
||||
}
|
||||
|
||||
if (razorLanguageServices == null)
|
||||
if (languageServices == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(razorLanguageServices));
|
||||
throw new ArgumentNullException(nameof(languageServices));
|
||||
}
|
||||
|
||||
if (workspace == null)
|
||||
|
|
@ -45,8 +45,9 @@ namespace Microsoft.CodeAnalysis.Host
|
|||
|
||||
_hostServices = hostServices;
|
||||
_workspaceServices = workspaceServices;
|
||||
_razorLanguageServices = new TestRazorLanguageServices(this, razorLanguageServices);
|
||||
_workspace = workspace;
|
||||
|
||||
_razorLanguageServices = new TestLanguageServices(this, languageServices);
|
||||
}
|
||||
|
||||
public override HostServices HostServices => _hostServices;
|
||||
|
|
@ -68,12 +69,13 @@ namespace Microsoft.CodeAnalysis.Host
|
|||
|
||||
public override HostLanguageServices GetLanguageServices(string languageName)
|
||||
{
|
||||
if (languageName != RazorLanguage.Name)
|
||||
if (languageName == RazorLanguage.Name)
|
||||
{
|
||||
throw new InvalidOperationException($"Test services do not support language service '{languageName}'. The only language services supported are '{RazorLanguage.Name}'.");
|
||||
return _razorLanguageServices;
|
||||
}
|
||||
|
||||
return _razorLanguageServices;
|
||||
// Fallback to default host services to resolve roslyn specific features.
|
||||
return DefaultWorkspace.Services.GetLanguageServices(languageName);
|
||||
}
|
||||
|
||||
public override IEnumerable<string> SupportedLanguages => new[] { RazorLanguage.Name };
|
||||
|
|
|
|||
|
|
@ -2,108 +2,119 @@
|
|||
// 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.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
public class DefaultProjectSnapshotTest
|
||||
{
|
||||
[Fact]
|
||||
public void WithWorkspaceProject_CreatesSnapshot_UpdatesUnderlyingProject()
|
||||
public DefaultProjectSnapshotTest()
|
||||
{
|
||||
// Arrange
|
||||
var hostProject = new HostProject("Test.cshtml", FallbackRazorConfiguration.MVC_2_0);
|
||||
var workspaceProject = GetWorkspaceProject("Test1");
|
||||
var original = new DefaultProjectSnapshot(hostProject, workspaceProject);
|
||||
TagHelperResolver = new TestTagHelperResolver();
|
||||
|
||||
var anotherProject = GetWorkspaceProject("Test1");
|
||||
|
||||
// Act
|
||||
var snapshot = original.WithWorkspaceProject(anotherProject);
|
||||
|
||||
// Assert
|
||||
Assert.Same(anotherProject, snapshot.WorkspaceProject);
|
||||
Assert.Equal(original.ComputedVersion, snapshot.ComputedVersion);
|
||||
Assert.Equal(original.Configuration, snapshot.Configuration);
|
||||
Assert.Equal(original.TagHelpers, snapshot.TagHelpers);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithProjectChange_WithProject_CreatesSnapshot_UpdatesValues()
|
||||
{
|
||||
// Arrange
|
||||
var hostProject = new HostProject("Test.cshtml", FallbackRazorConfiguration.MVC_2_0);
|
||||
var workspaceProject = GetWorkspaceProject("Test1");
|
||||
var original = new DefaultProjectSnapshot(hostProject, workspaceProject);
|
||||
|
||||
var anotherProject = GetWorkspaceProject("Test1");
|
||||
var update = new ProjectSnapshotUpdateContext(original.FilePath, hostProject, anotherProject, original.Version)
|
||||
{
|
||||
TagHelpers = Array.Empty<TagHelperDescriptor>(),
|
||||
};
|
||||
|
||||
// Act
|
||||
var snapshot = original.WithComputedUpdate(update);
|
||||
|
||||
// Assert
|
||||
Assert.Same(original.WorkspaceProject, snapshot.WorkspaceProject);
|
||||
Assert.Same(update.TagHelpers, snapshot.TagHelpers);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HaveTagHelpersChanged_NoUpdatesToTagHelpers_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var hostProject = new HostProject("Test1.csproj", RazorConfiguration.Default);
|
||||
var workspaceProject = GetWorkspaceProject("Test1");
|
||||
var original = new DefaultProjectSnapshot(hostProject, workspaceProject);
|
||||
|
||||
var anotherProject = GetWorkspaceProject("Test1");
|
||||
var update = new ProjectSnapshotUpdateContext("Test1.csproj", hostProject, anotherProject, VersionStamp.Default);
|
||||
var snapshot = original.WithComputedUpdate(update);
|
||||
|
||||
// Act
|
||||
var result = snapshot.HaveTagHelpersChanged(original);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HaveTagHelpersChanged_TagHelpersUpdated_ReturnsTrue()
|
||||
{
|
||||
// Arrange
|
||||
var hostProject = new HostProject("Test1.csproj", RazorConfiguration.Default);
|
||||
var workspaceProject = GetWorkspaceProject("Test1");
|
||||
var original = new DefaultProjectSnapshot(hostProject, workspaceProject);
|
||||
|
||||
var anotherProject = GetWorkspaceProject("Test1");
|
||||
var update = new ProjectSnapshotUpdateContext("Test1.csproj", hostProject, anotherProject, VersionStamp.Default)
|
||||
{
|
||||
TagHelpers = new[]
|
||||
HostServices = TestServices.Create(
|
||||
new IWorkspaceService[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("One", "TestAssembly").Build(),
|
||||
TagHelperDescriptorBuilder.Create("Two", "TestAssembly").Build(),
|
||||
new TestProjectSnapshotProjectEngineFactory(),
|
||||
},
|
||||
new ILanguageService[]
|
||||
{
|
||||
TagHelperResolver,
|
||||
});
|
||||
|
||||
HostProject = new HostProject("c:\\MyProject\\Test.csproj", FallbackRazorConfiguration.MVC_2_0);
|
||||
HostProjectWithConfigurationChange = new HostProject("c:\\MyProject\\Test.csproj", FallbackRazorConfiguration.MVC_1_0);
|
||||
|
||||
Workspace = TestWorkspace.Create(HostServices);
|
||||
|
||||
var projectId = ProjectId.CreateNewId("Test");
|
||||
var solution = Workspace.CurrentSolution.AddProject(ProjectInfo.Create(
|
||||
projectId,
|
||||
VersionStamp.Default,
|
||||
"Test",
|
||||
"Test",
|
||||
LanguageNames.CSharp,
|
||||
"c:\\MyProject\\Test.csproj"));
|
||||
WorkspaceProject = solution.GetProject(projectId);
|
||||
|
||||
SomeTagHelpers = new List<TagHelperDescriptor>();
|
||||
SomeTagHelpers.Add(TagHelperDescriptorBuilder.Create("Test1", "TestAssembly").Build());
|
||||
|
||||
Documents = new HostDocument[]
|
||||
{
|
||||
new HostDocument("c:\\MyProject\\File.cshtml", "File.cshtml"),
|
||||
new HostDocument("c:\\MyProject\\Index.cshtml", "Index.cshtml"),
|
||||
|
||||
// linked file
|
||||
new HostDocument("c:\\SomeOtherProject\\Index.cshtml", "Pages\\Index.cshtml"),
|
||||
};
|
||||
var snapshot = original.WithComputedUpdate(update);
|
||||
|
||||
// Act
|
||||
var result = snapshot.HaveTagHelpersChanged(original);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
private Project GetWorkspaceProject(string name)
|
||||
private HostDocument[] Documents { get; }
|
||||
|
||||
private HostProject HostProject { get; }
|
||||
|
||||
private HostProject HostProjectWithConfigurationChange { get; }
|
||||
|
||||
private Project WorkspaceProject { get; }
|
||||
|
||||
private TestTagHelperResolver TagHelperResolver { get; }
|
||||
|
||||
private HostServices HostServices { get; }
|
||||
|
||||
private Workspace Workspace { get; }
|
||||
|
||||
private List<TagHelperDescriptor> SomeTagHelpers { get; }
|
||||
|
||||
[Fact]
|
||||
public void ProjectSnapshot_CachesDocumentSnapshots()
|
||||
{
|
||||
Project project = null;
|
||||
TestWorkspace.Create(workspace =>
|
||||
// Arrange
|
||||
var state = new ProjectState(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.AddHostDocument(Documents[0])
|
||||
.AddHostDocument(Documents[1])
|
||||
.AddHostDocument(Documents[2]);
|
||||
var snapshot = new DefaultProjectSnapshot(state);
|
||||
|
||||
// Act
|
||||
var documents = snapshot.DocumentFilePaths.ToDictionary(f => f, f => snapshot.GetDocument(f));
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
documents,
|
||||
d => Assert.Same(d.Value, snapshot.GetDocument(d.Key)),
|
||||
d => Assert.Same(d.Value, snapshot.GetDocument(d.Key)),
|
||||
d => Assert.Same(d.Value, snapshot.GetDocument(d.Key)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectSnapshot_CachesTagHelperTask()
|
||||
{
|
||||
// Arrange
|
||||
TagHelperResolver.CompletionSource = new TaskCompletionSource<TagHelperResolutionResult>();
|
||||
|
||||
try
|
||||
{
|
||||
project = workspace.AddProject(name, LanguageNames.CSharp);
|
||||
});
|
||||
return project;
|
||||
var state = new ProjectState(Workspace.Services, HostProject, WorkspaceProject);
|
||||
var snapshot = new DefaultProjectSnapshot(state);
|
||||
|
||||
// Act
|
||||
var task1 = snapshot.GetTagHelpersAsync();
|
||||
var task2 = snapshot.GetTagHelpersAsync();
|
||||
|
||||
// Assert
|
||||
Assert.Same(task1, task2);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TagHelperResolver.CompletionSource.SetCanceled();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
// 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.Text;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
public class DocumentStateTest
|
||||
{
|
||||
public DocumentStateTest()
|
||||
{
|
||||
TagHelperResolver = new TestTagHelperResolver();
|
||||
|
||||
HostServices = TestServices.Create(
|
||||
new IWorkspaceService[]
|
||||
{
|
||||
new TestProjectSnapshotProjectEngineFactory(),
|
||||
},
|
||||
new ILanguageService[]
|
||||
{
|
||||
TagHelperResolver,
|
||||
});
|
||||
|
||||
HostProject = new HostProject("c:\\MyProject\\Test.csproj", FallbackRazorConfiguration.MVC_2_0);
|
||||
HostProjectWithConfigurationChange = new HostProject("c:\\MyProject\\Test.csproj", FallbackRazorConfiguration.MVC_1_0);
|
||||
|
||||
Workspace = TestWorkspace.Create(HostServices);
|
||||
|
||||
var projectId = ProjectId.CreateNewId("Test");
|
||||
var solution = Workspace.CurrentSolution.AddProject(ProjectInfo.Create(
|
||||
projectId,
|
||||
VersionStamp.Default,
|
||||
"Test",
|
||||
"Test",
|
||||
LanguageNames.CSharp,
|
||||
"c:\\MyProject\\Test.csproj"));
|
||||
WorkspaceProject = solution.GetProject(projectId);
|
||||
|
||||
SomeTagHelpers = new List<TagHelperDescriptor>();
|
||||
SomeTagHelpers.Add(TagHelperDescriptorBuilder.Create("Test1", "TestAssembly").Build());
|
||||
|
||||
Document = new HostDocument("c:\\MyProject\\File.cshtml", "File.cshtml");
|
||||
}
|
||||
|
||||
private HostDocument Document { get; }
|
||||
|
||||
private HostProject HostProject { get; }
|
||||
|
||||
private HostProject HostProjectWithConfigurationChange { get; }
|
||||
|
||||
private Project WorkspaceProject { get; }
|
||||
|
||||
private TestTagHelperResolver TagHelperResolver { get; }
|
||||
|
||||
private HostServices HostServices { get; }
|
||||
|
||||
private Workspace Workspace { get; }
|
||||
|
||||
private List<TagHelperDescriptor> SomeTagHelpers { get; }
|
||||
|
||||
[Fact]
|
||||
public void DocumentState_ConstructedNew()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
var state = new DocumentState(Workspace.Services, Document);
|
||||
|
||||
// Assert
|
||||
Assert.NotEqual(VersionStamp.Default, state.Version);
|
||||
}
|
||||
|
||||
[Fact] // There's no magic in the constructor.
|
||||
public void ProjectState_ConstructedFromCopy()
|
||||
{
|
||||
// Arrange
|
||||
var original = new DocumentState(Workspace.Services, Document);
|
||||
|
||||
// Act
|
||||
var state = new DocumentState(original, ProjectDifference.ConfigurationChanged);
|
||||
|
||||
// Assert
|
||||
Assert.NotEqual(original.Version, state.Version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,359 @@
|
|||
// 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.Text;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
public class ProjectStateTest
|
||||
{
|
||||
public ProjectStateTest()
|
||||
{
|
||||
TagHelperResolver = new TestTagHelperResolver();
|
||||
|
||||
HostServices = TestServices.Create(
|
||||
new IWorkspaceService[]
|
||||
{
|
||||
new TestProjectSnapshotProjectEngineFactory(),
|
||||
},
|
||||
new ILanguageService[]
|
||||
{
|
||||
TagHelperResolver,
|
||||
});
|
||||
|
||||
HostProject = new HostProject("c:\\MyProject\\Test.csproj", FallbackRazorConfiguration.MVC_2_0);
|
||||
HostProjectWithConfigurationChange = new HostProject("c:\\MyProject\\Test.csproj", FallbackRazorConfiguration.MVC_1_0);
|
||||
|
||||
Workspace = TestWorkspace.Create(HostServices);
|
||||
|
||||
var projectId = ProjectId.CreateNewId("Test");
|
||||
var solution = Workspace.CurrentSolution.AddProject(ProjectInfo.Create(
|
||||
projectId,
|
||||
VersionStamp.Default,
|
||||
"Test",
|
||||
"Test",
|
||||
LanguageNames.CSharp,
|
||||
"c:\\MyProject\\Test.csproj"));
|
||||
WorkspaceProject = solution.GetProject(projectId);
|
||||
|
||||
SomeTagHelpers = new List<TagHelperDescriptor>();
|
||||
SomeTagHelpers.Add(TagHelperDescriptorBuilder.Create("Test1", "TestAssembly").Build());
|
||||
|
||||
Documents = new HostDocument[]
|
||||
{
|
||||
new HostDocument("c:\\MyProject\\File.cshtml", "File.cshtml"),
|
||||
new HostDocument("c:\\MyProject\\Index.cshtml", "Index.cshtml"),
|
||||
|
||||
// linked file
|
||||
new HostDocument("c:\\SomeOtherProject\\Index.cshtml", "Pages\\Index.cshtml"),
|
||||
};
|
||||
}
|
||||
|
||||
private HostDocument[] Documents { get; }
|
||||
|
||||
private HostProject HostProject { get; }
|
||||
|
||||
private HostProject HostProjectWithConfigurationChange { get; }
|
||||
|
||||
private Project WorkspaceProject { get; }
|
||||
|
||||
private TestTagHelperResolver TagHelperResolver { get; }
|
||||
|
||||
private HostServices HostServices { get; }
|
||||
|
||||
private Workspace Workspace { get; }
|
||||
|
||||
private List<TagHelperDescriptor> SomeTagHelpers { get; }
|
||||
|
||||
[Fact]
|
||||
public void ProjectState_ConstructedNew()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
var state = new ProjectState(Workspace.Services, HostProject, WorkspaceProject);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(state.Documents);
|
||||
Assert.NotEqual(VersionStamp.Default, state.Version);
|
||||
}
|
||||
|
||||
[Fact] // There's no magic in the constructor.
|
||||
public void ProjectState_ConstructedFromCopy()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProjectState(Workspace.Services, HostProject, WorkspaceProject);
|
||||
|
||||
// Act
|
||||
var state = new ProjectState(original, ProjectDifference.None, HostProject, WorkspaceProject, original.Documents);
|
||||
|
||||
// Assert
|
||||
Assert.Same(original.Documents, state.Documents);
|
||||
Assert.NotEqual(original.Version, state.Version);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectState_AddHostDocument_ToEmpty()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProjectState(Workspace.Services, HostProject, WorkspaceProject);
|
||||
|
||||
// Act
|
||||
var state = original.AddHostDocument(Documents[0]);
|
||||
|
||||
// Assert
|
||||
Assert.NotEqual(original.Version, state.Version);
|
||||
|
||||
Assert.Collection(
|
||||
state.Documents.OrderBy(kvp => kvp.Key),
|
||||
d => Assert.Same(Documents[0], d.Value.HostDocument));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectState_AddHostDocument_ToProjectWithDocuments()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProjectState(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.AddHostDocument(Documents[2])
|
||||
.AddHostDocument(Documents[1]);
|
||||
|
||||
// Act
|
||||
var state = original.AddHostDocument(Documents[0]);
|
||||
|
||||
// Assert
|
||||
Assert.NotEqual(original.Version, state.Version);
|
||||
|
||||
Assert.Collection(
|
||||
state.Documents.OrderBy(kvp => kvp.Key),
|
||||
d => Assert.Same(Documents[0], d.Value.HostDocument),
|
||||
d => Assert.Same(Documents[1], d.Value.HostDocument),
|
||||
d => Assert.Same(Documents[2], d.Value.HostDocument));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectState_AddHostDocument_RetainsComputedState()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProjectState(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.AddHostDocument(Documents[2])
|
||||
.AddHostDocument(Documents[1]);
|
||||
|
||||
// Force init
|
||||
GC.KeepAlive(original.ProjectEngine);
|
||||
GC.KeepAlive(original.TagHelpers);
|
||||
|
||||
// Act
|
||||
var state = original.AddHostDocument(Documents[0]);
|
||||
|
||||
// Assert
|
||||
Assert.Same(original.ProjectEngine, state.ProjectEngine);
|
||||
Assert.Same(original.TagHelpers, state.TagHelpers);
|
||||
|
||||
Assert.Same(original.Documents[Documents[1].FilePath], state.Documents[Documents[1].FilePath]);
|
||||
Assert.Same(original.Documents[Documents[2].FilePath], state.Documents[Documents[2].FilePath]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectState_AddHostDocument_DuplicateNoops()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProjectState(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.AddHostDocument(Documents[2])
|
||||
.AddHostDocument(Documents[1]);
|
||||
|
||||
// Act
|
||||
var state = original.AddHostDocument(new HostDocument(Documents[1].FilePath, "SomePath.cshtml"));
|
||||
|
||||
// Assert
|
||||
Assert.Same(original, state);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectState_RemoveHostDocument_FromProjectWithDocuments()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProjectState(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.AddHostDocument(Documents[2])
|
||||
.AddHostDocument(Documents[1]);
|
||||
|
||||
// Act
|
||||
var state = original.RemoveHostDocument(Documents[1]);
|
||||
|
||||
// Assert
|
||||
Assert.NotEqual(original.Version, state.Version);
|
||||
|
||||
Assert.Collection(
|
||||
state.Documents.OrderBy(kvp => kvp.Key),
|
||||
d => Assert.Same(Documents[2], d.Value.HostDocument));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectState_RemoveHostDocument_RetainsComputedState()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProjectState(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.AddHostDocument(Documents[2])
|
||||
.AddHostDocument(Documents[1]);
|
||||
|
||||
// Force init
|
||||
GC.KeepAlive(original.ProjectEngine);
|
||||
GC.KeepAlive(original.TagHelpers);
|
||||
|
||||
// Act
|
||||
var state = original.RemoveHostDocument(Documents[2]);
|
||||
|
||||
// Assert
|
||||
Assert.Same(original.ProjectEngine, state.ProjectEngine);
|
||||
Assert.Same(original.TagHelpers, state.TagHelpers);
|
||||
|
||||
Assert.Same(original.Documents[Documents[1].FilePath], state.Documents[Documents[1].FilePath]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectState_RemoveHostDocument_NotFoundNoops()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProjectState(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.AddHostDocument(Documents[2])
|
||||
.AddHostDocument(Documents[1]);
|
||||
|
||||
// Act
|
||||
var state = original.RemoveHostDocument(Documents[0]);
|
||||
|
||||
// Assert
|
||||
Assert.Same(original, state);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectState_WithHostProject_ConfigurationChange_UpdatesComputedState()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProjectState(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.AddHostDocument(Documents[2])
|
||||
.AddHostDocument(Documents[1]);
|
||||
|
||||
// Force init
|
||||
GC.KeepAlive(original.ProjectEngine);
|
||||
GC.KeepAlive(original.TagHelpers);
|
||||
|
||||
// Act
|
||||
var state = original.WithHostProject(HostProjectWithConfigurationChange);
|
||||
|
||||
// Assert
|
||||
Assert.NotEqual(original.Version, state.Version);
|
||||
Assert.Same(HostProjectWithConfigurationChange, state.HostProject);
|
||||
|
||||
Assert.NotSame(original.ProjectEngine, state.ProjectEngine);
|
||||
Assert.NotSame(original.TagHelpers, state.TagHelpers);
|
||||
|
||||
Assert.NotSame(original.Documents[Documents[1].FilePath], state.Documents[Documents[1].FilePath]);
|
||||
Assert.NotSame(original.Documents[Documents[1].FilePath], state.Documents[Documents[1].FilePath]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectState_WithHostProject_NoConfigurationChange_Noops()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProjectState(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.AddHostDocument(Documents[2])
|
||||
.AddHostDocument(Documents[1]);
|
||||
|
||||
// Force init
|
||||
GC.KeepAlive(original.ProjectEngine);
|
||||
GC.KeepAlive(original.TagHelpers);
|
||||
|
||||
// Act
|
||||
var state = original.WithHostProject(HostProject);
|
||||
|
||||
// Assert
|
||||
Assert.Same(original, state);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectState_WithWorkspaceProject_Removed()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProjectState(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.AddHostDocument(Documents[2])
|
||||
.AddHostDocument(Documents[1]);
|
||||
|
||||
// Force init
|
||||
GC.KeepAlive(original.ProjectEngine);
|
||||
GC.KeepAlive(original.TagHelpers);
|
||||
|
||||
// Act
|
||||
var state = original.WithWorkspaceProject(null);
|
||||
|
||||
// Assert
|
||||
Assert.NotEqual(original.Version, state.Version);
|
||||
Assert.Null(state.WorkspaceProject);
|
||||
|
||||
Assert.Same(original.ProjectEngine, state.ProjectEngine);
|
||||
Assert.NotSame(original.TagHelpers, state.TagHelpers);
|
||||
|
||||
Assert.NotSame(original.Documents[Documents[1].FilePath], state.Documents[Documents[1].FilePath]);
|
||||
Assert.NotSame(original.Documents[Documents[1].FilePath], state.Documents[Documents[1].FilePath]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectState_WithWorkspaceProject_Added()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProjectState(Workspace.Services, HostProject, null)
|
||||
.AddHostDocument(Documents[2])
|
||||
.AddHostDocument(Documents[1]);
|
||||
|
||||
// Force init
|
||||
GC.KeepAlive(original.ProjectEngine);
|
||||
GC.KeepAlive(original.TagHelpers);
|
||||
|
||||
// Act
|
||||
var state = original.WithWorkspaceProject(WorkspaceProject);
|
||||
|
||||
// Assert
|
||||
Assert.NotEqual(original.Version, state.Version);
|
||||
Assert.Same(WorkspaceProject, state.WorkspaceProject);
|
||||
|
||||
Assert.Same(original.ProjectEngine, state.ProjectEngine);
|
||||
Assert.NotSame(original.TagHelpers, state.TagHelpers);
|
||||
|
||||
Assert.NotSame(original.Documents[Documents[1].FilePath], state.Documents[Documents[1].FilePath]);
|
||||
Assert.NotSame(original.Documents[Documents[1].FilePath], state.Documents[Documents[1].FilePath]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectState_WithWorkspaceProject_Changed()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProjectState(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.AddHostDocument(Documents[2])
|
||||
.AddHostDocument(Documents[1]);
|
||||
|
||||
// Force init
|
||||
GC.KeepAlive(original.ProjectEngine);
|
||||
GC.KeepAlive(original.TagHelpers);
|
||||
|
||||
var changed = WorkspaceProject.WithAssemblyName("Test1");
|
||||
|
||||
// Act
|
||||
var state = original.WithWorkspaceProject(changed);
|
||||
|
||||
// Assert
|
||||
Assert.NotEqual(original.Version, state.Version);
|
||||
Assert.Same(changed, state.WorkspaceProject);
|
||||
|
||||
Assert.Same(original.ProjectEngine, state.ProjectEngine);
|
||||
Assert.NotSame(original.TagHelpers, state.TagHelpers);
|
||||
|
||||
Assert.NotSame(original.Documents[Documents[1].FilePath], state.Documents[Documents[1].FilePath]);
|
||||
Assert.NotSame(original.Documents[Documents[1].FilePath], state.Documents[Documents[1].FilePath]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,8 +3,6 @@
|
|||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -214,23 +212,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
private class TestProjectSnapshotManager : DefaultProjectSnapshotManager
|
||||
{
|
||||
public TestProjectSnapshotManager(IEnumerable<ProjectSnapshotChangeTrigger> triggers, Workspace workspace)
|
||||
: base(Mock.Of<ForegroundDispatcher>(), Mock.Of<ErrorReporter>(), new TestProjectSnapshotWorker(), triggers, workspace)
|
||||
: base(Mock.Of<ForegroundDispatcher>(), Mock.Of<ErrorReporter>(), triggers, workspace)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void NotifyBackgroundWorker(ProjectSnapshotUpdateContext context)
|
||||
{
|
||||
Assert.NotNull(context.HostProject);
|
||||
Assert.NotNull(context.WorkspaceProject);
|
||||
}
|
||||
}
|
||||
|
||||
private class TestProjectSnapshotWorker : ProjectSnapshotWorker
|
||||
{
|
||||
public override Task ProcessUpdateAsync(ProjectSnapshotUpdateContext update, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.Linq;
|
||||
using Moq;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal class TestProjectSnapshotManager : DefaultProjectSnapshotManager
|
||||
{
|
||||
public TestProjectSnapshotManager(Workspace workspace)
|
||||
: base(Mock.Of<ForegroundDispatcher>(), Mock.Of<ErrorReporter>(), Enumerable.Empty<ProjectSnapshotChangeTrigger>(), workspace)
|
||||
{
|
||||
}
|
||||
|
||||
public TestProjectSnapshotManager(ForegroundDispatcher foregroundDispatcher, Workspace workspace)
|
||||
: base(foregroundDispatcher, Mock.Of<ErrorReporter>(), Enumerable.Empty<ProjectSnapshotChangeTrigger>(), workspace)
|
||||
{
|
||||
}
|
||||
|
||||
public bool AllowNotifyListeners { get; set; }
|
||||
|
||||
public DefaultProjectSnapshot GetSnapshot(HostProject hostProject)
|
||||
{
|
||||
return Projects.Cast<DefaultProjectSnapshot>().FirstOrDefault(s => s.FilePath == hostProject.FilePath);
|
||||
}
|
||||
|
||||
public DefaultProjectSnapshot GetSnapshot(Project workspaceProject)
|
||||
{
|
||||
return Projects.Cast<DefaultProjectSnapshot>().FirstOrDefault(s => s.FilePath == workspaceProject.FilePath);
|
||||
}
|
||||
|
||||
protected override void NotifyListeners(ProjectChangeEventArgs e)
|
||||
{
|
||||
if (AllowNotifyListeners)
|
||||
{
|
||||
base.NotifyListeners(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal class TestProjectSnapshotProjectEngineFactory : ProjectSnapshotProjectEngineFactory
|
||||
{
|
||||
public RazorProjectEngine Engine { get; set; }
|
||||
|
||||
public override RazorProjectEngine Create(ProjectSnapshot project, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
|
||||
{
|
||||
return Engine ?? RazorProjectEngine.Create(project.Configuration, fileSystem, configure);
|
||||
}
|
||||
|
||||
public override IProjectEngineFactory FindFactory(ProjectSnapshot project)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override IProjectEngineFactory FindSerializableFactory(ProjectSnapshot project)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
internal class TestTagHelperResolver : TagHelperResolver
|
||||
{
|
||||
public TaskCompletionSource<TagHelperResolutionResult> CompletionSource { get; set; }
|
||||
|
||||
public IList<TagHelperDescriptor> TagHelpers { get; } = new List<TagHelperDescriptor>();
|
||||
|
||||
public override Task<TagHelperResolutionResult> GetTagHelpersAsync(ProjectSnapshot project, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (CompletionSource == null)
|
||||
{
|
||||
return Task.FromResult(new TagHelperResolutionResult(TagHelpers.ToArray(), Array.Empty<RazorDiagnostic>()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return CompletionSource.Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
|
|
@ -13,23 +14,51 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
{
|
||||
public class DefaultImportDocumentManagerIntegrationTest : ForegroundDispatcherTestBase
|
||||
{
|
||||
public DefaultImportDocumentManagerIntegrationTest()
|
||||
{
|
||||
ProjectPath = "C:\\path\\to\\project\\project.csproj";
|
||||
|
||||
FileSystem = RazorProjectFileSystem.Create(Path.GetDirectoryName(ProjectPath));
|
||||
ProjectEngine = RazorProjectEngine.Create(FallbackRazorConfiguration.MVC_2_1, FileSystem, b =>
|
||||
{
|
||||
// These tests rely on MVC's import behavior.
|
||||
Microsoft.AspNetCore.Mvc.Razor.Extensions.RazorExtensions.Register(b);
|
||||
});
|
||||
}
|
||||
|
||||
private string FilePath { get; }
|
||||
|
||||
private string ProjectPath { get; }
|
||||
|
||||
private RazorProjectFileSystem FileSystem { get; }
|
||||
|
||||
private RazorProjectEngine ProjectEngine { get; }
|
||||
|
||||
[ForegroundFact]
|
||||
public void Changed_TrackerChanged_ResultsInChangedHavingCorrectArgs()
|
||||
{
|
||||
// Arrange
|
||||
var filePath = "C:\\path\\to\\project\\Views\\Home\\file.cshtml";
|
||||
var anotherFilePath = "C:\\path\\to\\project\\anotherFile.cshtml";
|
||||
var projectPath = "C:\\path\\to\\project\\project.csproj";
|
||||
var testImportsPath = "C:\\path\\to\\project\\_ViewImports.cshtml";
|
||||
var tracker = Mock.Of<VisualStudioDocumentTracker>(t => t.FilePath == filePath && t.ProjectPath == projectPath);
|
||||
var anotherTracker = Mock.Of<VisualStudioDocumentTracker>(t => t.FilePath == anotherFilePath && t.ProjectPath == projectPath);
|
||||
var projectEngineFactoryService = GetProjectEngineFactoryService();
|
||||
var fileChangeTracker = new Mock<FileChangeTracker>();
|
||||
fileChangeTracker.Setup(f => f.FilePath).Returns(testImportsPath);
|
||||
|
||||
var tracker = Mock.Of<VisualStudioDocumentTracker>(
|
||||
t => t.FilePath == "C:\\path\\to\\project\\Views\\Home\\file.cshtml" &&
|
||||
t.ProjectPath == ProjectPath &&
|
||||
t.ProjectSnapshot == Mock.Of<ProjectSnapshot>(p => p.GetProjectEngine() == ProjectEngine));
|
||||
|
||||
var anotherTracker = Mock.Of<VisualStudioDocumentTracker>(
|
||||
t => t.FilePath == "C:\\path\\to\\project\\anotherFile.cshtml" &&
|
||||
t.ProjectPath == ProjectPath &&
|
||||
t.ProjectSnapshot == Mock.Of<ProjectSnapshot>(p => p.GetProjectEngine() == ProjectEngine));
|
||||
|
||||
var fileChangeTrackerFactory = new Mock<FileChangeTrackerFactory>();
|
||||
var fileChangeTracker = new Mock<FileChangeTracker>();
|
||||
fileChangeTracker
|
||||
.Setup(f => f.FilePath)
|
||||
.Returns(testImportsPath);
|
||||
fileChangeTrackerFactory
|
||||
.Setup(f => f.Create(testImportsPath))
|
||||
.Returns(fileChangeTracker.Object);
|
||||
|
||||
fileChangeTrackerFactory
|
||||
.Setup(f => f.Create("C:\\path\\to\\project\\Views\\_ViewImports.cshtml"))
|
||||
.Returns(Mock.Of<FileChangeTracker>());
|
||||
|
|
@ -38,7 +67,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
.Returns(Mock.Of<FileChangeTracker>());
|
||||
|
||||
var called = false;
|
||||
var manager = new DefaultImportDocumentManager(Dispatcher, new DefaultErrorReporter(), fileChangeTrackerFactory.Object, projectEngineFactoryService);
|
||||
var manager = new DefaultImportDocumentManager(Dispatcher, new DefaultErrorReporter(), fileChangeTrackerFactory.Object);
|
||||
manager.OnSubscribed(tracker);
|
||||
manager.OnSubscribed(anotherTracker);
|
||||
manager.Changed += (sender, args) =>
|
||||
|
|
@ -49,8 +78,8 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
Assert.Equal(FileChangeKind.Changed, args.Kind);
|
||||
Assert.Collection(
|
||||
args.AssociatedDocuments,
|
||||
f => Assert.Equal(filePath, f),
|
||||
f => Assert.Equal(anotherFilePath, f));
|
||||
f => Assert.Equal("C:\\path\\to\\project\\Views\\Home\\file.cshtml", f),
|
||||
f => Assert.Equal("C:\\path\\to\\project\\anotherFile.cshtml", f));
|
||||
};
|
||||
|
||||
// Act
|
||||
|
|
@ -59,25 +88,5 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
// Assert
|
||||
Assert.True(called);
|
||||
}
|
||||
|
||||
private RazorProjectEngineFactoryService GetProjectEngineFactoryService()
|
||||
{
|
||||
var projectManager = new Mock<ProjectSnapshotManager>();
|
||||
projectManager.Setup(p => p.Projects).Returns(Array.Empty<ProjectSnapshot>());
|
||||
|
||||
var projectEngineFactory = new Mock<IFallbackProjectEngineFactory>();
|
||||
projectEngineFactory.Setup(s => s.Create(It.IsAny<RazorConfiguration>(), It.IsAny<RazorProjectFileSystem>(), It.IsAny<Action<RazorProjectEngineBuilder>>()))
|
||||
.Returns<RazorConfiguration, RazorProjectFileSystem, Action<RazorProjectEngineBuilder>>(
|
||||
(c, fs, b) => RazorProjectEngine.Create(
|
||||
RazorConfiguration.Default,
|
||||
fs,
|
||||
builder => RazorExtensions.Register(builder)));
|
||||
|
||||
var service = new DefaultProjectEngineFactoryService(
|
||||
projectManager.Object,
|
||||
projectEngineFactory.Object,
|
||||
new Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[0]);
|
||||
return service;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
|
@ -13,23 +13,48 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
{
|
||||
public class DefaultImportDocumentManagerTest : ForegroundDispatcherTestBase
|
||||
{
|
||||
public DefaultImportDocumentManagerTest()
|
||||
{
|
||||
ProjectPath = "C:\\path\\to\\project\\project.csproj";
|
||||
|
||||
FileSystem = RazorProjectFileSystem.Create(Path.GetDirectoryName(ProjectPath));
|
||||
ProjectEngine = RazorProjectEngine.Create(FallbackRazorConfiguration.MVC_2_1, FileSystem, b =>
|
||||
{
|
||||
// These tests rely on MVC's import behavior.
|
||||
Microsoft.AspNetCore.Mvc.Razor.Extensions.RazorExtensions.Register(b);
|
||||
});
|
||||
}
|
||||
|
||||
private string FilePath { get; }
|
||||
|
||||
private string ProjectPath { get; }
|
||||
|
||||
private RazorProjectFileSystem FileSystem { get; }
|
||||
|
||||
private RazorProjectEngine ProjectEngine { get; }
|
||||
|
||||
[ForegroundFact]
|
||||
public void OnSubscribed_StartsFileChangeTrackers()
|
||||
{
|
||||
// Arrange
|
||||
var filePath = "C:\\path\\to\\project\\Views\\Home\\file.cshtml";
|
||||
var projectPath = "C:\\path\\to\\project\\project.csproj";
|
||||
var tracker = Mock.Of<VisualStudioDocumentTracker>(t => t.FilePath == filePath && t.ProjectPath == projectPath);
|
||||
var projectEngineService = GetProjectEngineFactoryService();
|
||||
var fileChangeTracker1 = new Mock<FileChangeTracker>();
|
||||
fileChangeTracker1.Setup(f => f.StartListening()).Verifiable();
|
||||
var tracker = Mock.Of<VisualStudioDocumentTracker>(
|
||||
t => t.FilePath == "C:\\path\\to\\project\\Views\\Home\\file.cshtml" &&
|
||||
t.ProjectPath == ProjectPath &&
|
||||
t.ProjectSnapshot == Mock.Of<ProjectSnapshot>(p => p.GetProjectEngine() == ProjectEngine));
|
||||
|
||||
var fileChangeTrackerFactory = new Mock<FileChangeTrackerFactory>();
|
||||
var fileChangeTracker1 = new Mock<FileChangeTracker>();
|
||||
fileChangeTracker1
|
||||
.Setup(f => f.StartListening())
|
||||
.Verifiable();
|
||||
fileChangeTrackerFactory
|
||||
.Setup(f => f.Create("C:\\path\\to\\project\\Views\\Home\\_ViewImports.cshtml"))
|
||||
.Returns(fileChangeTracker1.Object)
|
||||
.Verifiable();
|
||||
var fileChangeTracker2 = new Mock<FileChangeTracker>();
|
||||
fileChangeTracker2.Setup(f => f.StartListening()).Verifiable();
|
||||
fileChangeTracker2
|
||||
.Setup(f => f.StartListening())
|
||||
.Verifiable();
|
||||
fileChangeTrackerFactory
|
||||
.Setup(f => f.Create("C:\\path\\to\\project\\Views\\_ViewImports.cshtml"))
|
||||
.Returns(fileChangeTracker2.Object)
|
||||
|
|
@ -41,7 +66,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
.Returns(fileChangeTracker3.Object)
|
||||
.Verifiable();
|
||||
|
||||
var manager = new DefaultImportDocumentManager(Dispatcher, new DefaultErrorReporter(), fileChangeTrackerFactory.Object, projectEngineService);
|
||||
var manager = new DefaultImportDocumentManager(Dispatcher, new DefaultErrorReporter(), fileChangeTrackerFactory.Object);
|
||||
|
||||
// Act
|
||||
manager.OnSubscribed(tracker);
|
||||
|
|
@ -57,10 +82,15 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
public void OnSubscribed_AlreadySubscribed_DoesNothing()
|
||||
{
|
||||
// Arrange
|
||||
var filePath = "C:\\path\\to\\project\\file.cshtml";
|
||||
var projectPath = "C:\\path\\to\\project\\project.csproj";
|
||||
var tracker = Mock.Of<VisualStudioDocumentTracker>(t => t.FilePath == filePath && t.ProjectPath == projectPath);
|
||||
var projectEngineService = GetProjectEngineFactoryService();
|
||||
var tracker = Mock.Of<VisualStudioDocumentTracker>(
|
||||
t => t.FilePath == "C:\\path\\to\\project\\file.cshtml" &&
|
||||
t.ProjectPath == ProjectPath &&
|
||||
t.ProjectSnapshot == Mock.Of<ProjectSnapshot>(p => p.GetProjectEngine() == ProjectEngine));
|
||||
|
||||
var anotherTracker = Mock.Of<VisualStudioDocumentTracker>(
|
||||
t => t.FilePath == "C:\\path\\to\\project\\anotherFile.cshtml" &&
|
||||
t.ProjectPath == ProjectPath &&
|
||||
t.ProjectSnapshot == Mock.Of<ProjectSnapshot>(p => p.GetProjectEngine() == ProjectEngine));
|
||||
|
||||
var callCount = 0;
|
||||
var fileChangeTrackerFactory = new Mock<FileChangeTrackerFactory>();
|
||||
|
|
@ -69,12 +99,9 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
.Returns(Mock.Of<FileChangeTracker>())
|
||||
.Callback(() => callCount++);
|
||||
|
||||
var manager = new DefaultImportDocumentManager(Dispatcher, new DefaultErrorReporter(), fileChangeTrackerFactory.Object, projectEngineService);
|
||||
var manager = new DefaultImportDocumentManager(Dispatcher, new DefaultErrorReporter(), fileChangeTrackerFactory.Object);
|
||||
manager.OnSubscribed(tracker); // Start tracking the import.
|
||||
|
||||
var anotherFilePath = "C:\\path\\to\\project\\anotherFile.cshtml";
|
||||
var anotherTracker = Mock.Of<VisualStudioDocumentTracker>(t => t.FilePath == anotherFilePath && t.ProjectPath == projectPath);
|
||||
|
||||
// Act
|
||||
manager.OnSubscribed(anotherTracker);
|
||||
|
||||
|
|
@ -86,20 +113,22 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
public void OnUnsubscribed_StopsFileChangeTracker()
|
||||
{
|
||||
// Arrange
|
||||
var filePath = "C:\\path\\to\\project\\file.cshtml";
|
||||
var projectPath = "C:\\path\\to\\project\\project.csproj";
|
||||
var tracker = Mock.Of<VisualStudioDocumentTracker>(t => t.FilePath == filePath && t.ProjectPath == projectPath);
|
||||
var projectEngineService = GetProjectEngineFactoryService();
|
||||
var tracker = Mock.Of<VisualStudioDocumentTracker>(
|
||||
t => t.FilePath == "C:\\path\\to\\project\\file.cshtml" &&
|
||||
t.ProjectPath == ProjectPath &&
|
||||
t.ProjectSnapshot == Mock.Of<ProjectSnapshot>(p => p.GetProjectEngine() == ProjectEngine));
|
||||
|
||||
var fileChangeTracker = new Mock<FileChangeTracker>();
|
||||
fileChangeTracker.Setup(f => f.StopListening()).Verifiable();
|
||||
var fileChangeTrackerFactory = new Mock<FileChangeTrackerFactory>(MockBehavior.Strict);
|
||||
var fileChangeTracker = new Mock<FileChangeTracker>();
|
||||
fileChangeTracker
|
||||
.Setup(f => f.StopListening())
|
||||
.Verifiable();
|
||||
fileChangeTrackerFactory
|
||||
.Setup(f => f.Create("C:\\path\\to\\project\\_ViewImports.cshtml"))
|
||||
.Returns(fileChangeTracker.Object)
|
||||
.Verifiable();
|
||||
|
||||
var manager = new DefaultImportDocumentManager(Dispatcher, new DefaultErrorReporter(), fileChangeTrackerFactory.Object, projectEngineService);
|
||||
var manager = new DefaultImportDocumentManager(Dispatcher, new DefaultErrorReporter(), fileChangeTrackerFactory.Object);
|
||||
manager.OnSubscribed(tracker); // Start tracking the import.
|
||||
|
||||
// Act
|
||||
|
|
@ -114,49 +143,33 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
public void OnUnsubscribed_AnotherDocumentTrackingImport_DoesNotStopFileChangeTracker()
|
||||
{
|
||||
// Arrange
|
||||
var filePath = "C:\\path\\to\\project\\file.cshtml";
|
||||
var projectPath = "C:\\path\\to\\project\\project.csproj";
|
||||
var tracker = Mock.Of<VisualStudioDocumentTracker>(t => t.FilePath == filePath && t.ProjectPath == projectPath);
|
||||
var projectEngineService = GetProjectEngineFactoryService();
|
||||
var tracker = Mock.Of<VisualStudioDocumentTracker>(
|
||||
t => t.FilePath == "C:\\path\\to\\project\\file.cshtml" &&
|
||||
t.ProjectPath == ProjectPath &&
|
||||
t.ProjectSnapshot == Mock.Of<ProjectSnapshot>(p => p.GetProjectEngine() == ProjectEngine));
|
||||
|
||||
var anotherTracker = Mock.Of<VisualStudioDocumentTracker>(
|
||||
t => t.FilePath == "C:\\path\\to\\project\\anotherFile.cshtml" &&
|
||||
t.ProjectPath == ProjectPath &&
|
||||
t.ProjectSnapshot == Mock.Of<ProjectSnapshot>(p => p.GetProjectEngine() == ProjectEngine));
|
||||
|
||||
var fileChangeTrackerFactory = new Mock<FileChangeTrackerFactory>();
|
||||
var fileChangeTracker = new Mock<FileChangeTracker>();
|
||||
fileChangeTracker
|
||||
.Setup(f => f.StopListening())
|
||||
.Throws(new InvalidOperationException());
|
||||
var fileChangeTrackerFactory = new Mock<FileChangeTrackerFactory>();
|
||||
fileChangeTrackerFactory
|
||||
.Setup(f => f.Create(It.IsAny<string>()))
|
||||
.Returns(fileChangeTracker.Object);
|
||||
|
||||
var manager = new DefaultImportDocumentManager(Dispatcher, new DefaultErrorReporter(), fileChangeTrackerFactory.Object, projectEngineService);
|
||||
var manager = new DefaultImportDocumentManager(Dispatcher, new DefaultErrorReporter(), fileChangeTrackerFactory.Object);
|
||||
manager.OnSubscribed(tracker); // Starts tracking import for the first document.
|
||||
|
||||
var anotherFilePath = "C:\\path\\to\\project\\anotherFile.cshtml";
|
||||
var anotherTracker = Mock.Of<VisualStudioDocumentTracker>(t => t.FilePath == anotherFilePath && t.ProjectPath == projectPath);
|
||||
manager.OnSubscribed(anotherTracker); // Starts tracking import for the second document.
|
||||
|
||||
// Act & Assert (Does not throw)
|
||||
manager.OnUnsubscribed(tracker);
|
||||
}
|
||||
|
||||
private RazorProjectEngineFactoryService GetProjectEngineFactoryService()
|
||||
{
|
||||
var projectManager = new Mock<ProjectSnapshotManager>();
|
||||
projectManager.Setup(p => p.Projects).Returns(Array.Empty<ProjectSnapshot>());
|
||||
|
||||
var projectEngineFactory = new Mock<IFallbackProjectEngineFactory>();
|
||||
projectEngineFactory.Setup(s => s.Create(It.IsAny<RazorConfiguration>(), It.IsAny<RazorProjectFileSystem>(), It.IsAny<Action<RazorProjectEngineBuilder>>()))
|
||||
.Returns<RazorConfiguration, RazorProjectFileSystem, Action<RazorProjectEngineBuilder>>(
|
||||
(c, fs, b) => RazorProjectEngine.Create(
|
||||
RazorConfiguration.Default,
|
||||
fs,
|
||||
builder => RazorExtensions.Register(builder)));
|
||||
|
||||
var service = new DefaultProjectEngineFactoryService(
|
||||
projectManager.Object,
|
||||
projectEngineFactory.Object,
|
||||
new Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[0]);
|
||||
return service;
|
||||
manager.OnUnsubscribed(tracker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,19 +5,19 @@ using System;
|
|||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Moq;
|
||||
using Microsoft.VisualStudio.Editor.Razor;
|
||||
using Xunit;
|
||||
|
||||
using Mvc1_X = Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X;
|
||||
using MvcLatest = Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
public class DefaultProjectEngineFactoryServiceTest
|
||||
// Testing this here because we need references to the MVC factories.
|
||||
public class DefaultProjectSnapshotProjectEngineFactoryTest
|
||||
{
|
||||
public DefaultProjectEngineFactoryServiceTest()
|
||||
public DefaultProjectSnapshotProjectEngineFactoryTest()
|
||||
{
|
||||
Project project = null;
|
||||
|
||||
|
|
@ -34,12 +34,17 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
HostProject_For_2_0 = new HostProject("/TestPath/SomePath/Test.csproj", FallbackRazorConfiguration.MVC_2_0);
|
||||
|
||||
HostProject_For_2_1 = new HostProject(
|
||||
"/TestPath/SomePath/Test.csproj",
|
||||
"/TestPath/SomePath/Test.csproj",
|
||||
new ProjectSystemRazorConfiguration(RazorLanguageVersion.Version_2_1, "MVC-2.1", Array.Empty<RazorExtension>()));
|
||||
HostProject_For_UnknownConfiguration = new HostProject(
|
||||
"/TestPath/SomePath/Test.csproj",
|
||||
new ProjectSystemRazorConfiguration(RazorLanguageVersion.Version_2_1, "Blazor-0.1", Array.Empty<RazorExtension>()));
|
||||
|
||||
Snapshot_For_1_0 = new DefaultProjectSnapshot(new ProjectState(Workspace.Services, HostProject_For_1_0, WorkspaceProject));
|
||||
Snapshot_For_1_1 = new DefaultProjectSnapshot(new ProjectState(Workspace.Services, HostProject_For_1_1, WorkspaceProject));
|
||||
Snapshot_For_2_0 = new DefaultProjectSnapshot(new ProjectState(Workspace.Services, HostProject_For_2_0, WorkspaceProject));
|
||||
Snapshot_For_2_1 = new DefaultProjectSnapshot(new ProjectState(Workspace.Services, HostProject_For_2_1, WorkspaceProject));
|
||||
Snapshot_For_UnknownConfiguration = new DefaultProjectSnapshot(new ProjectState(Workspace.Services, HostProject_For_UnknownConfiguration, WorkspaceProject));
|
||||
|
||||
CustomFactories = new Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[]
|
||||
{
|
||||
|
|
@ -74,7 +79,16 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
|
||||
private HostProject HostProject_For_UnknownConfiguration { get; }
|
||||
|
||||
// We don't actually look at the project, we rely on the ProjectStateManager
|
||||
private ProjectSnapshot Snapshot_For_1_0 { get; }
|
||||
|
||||
private ProjectSnapshot Snapshot_For_1_1 { get; }
|
||||
|
||||
private ProjectSnapshot Snapshot_For_2_0 { get; }
|
||||
|
||||
private ProjectSnapshot Snapshot_For_2_1 { get; }
|
||||
|
||||
private ProjectSnapshot Snapshot_For_UnknownConfiguration { get; }
|
||||
|
||||
private Project WorkspaceProject { get; }
|
||||
|
||||
private Workspace Workspace { get; }
|
||||
|
|
@ -83,14 +97,12 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
public void Create_CreatesDesignTimeTemplateEngine_ForVersion2_1()
|
||||
{
|
||||
// Arrange
|
||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
||||
projectManager.HostProjectAdded(HostProject_For_2_1);
|
||||
projectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
var snapshot = Snapshot_For_2_1;
|
||||
|
||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager, FallbackFactory, CustomFactories);
|
||||
var factory = new DefaultProjectSnapshotProjectEngineFactory(FallbackFactory, CustomFactories);
|
||||
|
||||
// Act
|
||||
var engine = factoryService.Create("/TestPath/SomePath/", b =>
|
||||
var engine = factory.Create(snapshot, b =>
|
||||
{
|
||||
b.Features.Add(new MyCoolNewFeature());
|
||||
});
|
||||
|
|
@ -106,14 +118,12 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
public void Create_CreatesDesignTimeTemplateEngine_ForVersion2_0()
|
||||
{
|
||||
// Arrange
|
||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
||||
projectManager.HostProjectAdded(HostProject_For_2_0);
|
||||
projectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
var snapshot = Snapshot_For_2_0;
|
||||
|
||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager, FallbackFactory, CustomFactories);
|
||||
var factory = new DefaultProjectSnapshotProjectEngineFactory(FallbackFactory, CustomFactories);
|
||||
|
||||
// Act
|
||||
var engine = factoryService.Create("/TestPath/SomePath/", b =>
|
||||
var engine = factory.Create(snapshot, b =>
|
||||
{
|
||||
b.Features.Add(new MyCoolNewFeature());
|
||||
});
|
||||
|
|
@ -129,14 +139,12 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
public void Create_CreatesTemplateEngine_ForVersion1_1()
|
||||
{
|
||||
// Arrange
|
||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
||||
projectManager.HostProjectAdded(HostProject_For_1_1);
|
||||
projectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
var snapshot = Snapshot_For_1_1;
|
||||
|
||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager, FallbackFactory, CustomFactories);
|
||||
var factory = new DefaultProjectSnapshotProjectEngineFactory(FallbackFactory, CustomFactories);
|
||||
|
||||
// Act
|
||||
var engine = factoryService.Create("/TestPath/SomePath/", b =>
|
||||
var engine = factory.Create(snapshot, b =>
|
||||
{
|
||||
b.Features.Add(new MyCoolNewFeature());
|
||||
});
|
||||
|
|
@ -152,14 +160,12 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
public void Create_DoesNotSupportViewComponentTagHelpers_ForVersion1_0()
|
||||
{
|
||||
// Arrange
|
||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
||||
projectManager.HostProjectAdded(HostProject_For_1_0);
|
||||
projectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
var snapshot = Snapshot_For_1_0;
|
||||
|
||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager, FallbackFactory, CustomFactories);
|
||||
var factory = new DefaultProjectSnapshotProjectEngineFactory(FallbackFactory, CustomFactories);
|
||||
|
||||
// Act
|
||||
var engine = factoryService.Create("/TestPath/SomePath/", b =>
|
||||
var engine = factory.Create(snapshot, b =>
|
||||
{
|
||||
b.Features.Add(new MyCoolNewFeature());
|
||||
});
|
||||
|
|
@ -171,40 +177,15 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
Assert.Empty(engine.Engine.Features.OfType<MvcLatest.ViewComponentTagHelperPass>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Create_UnknownProject_UsesVersion2_0()
|
||||
{
|
||||
// Arrange
|
||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
||||
|
||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager, FallbackFactory, CustomFactories);
|
||||
|
||||
// Act
|
||||
var engine = factoryService.Create("/TestPath/DifferentPath/", b =>
|
||||
{
|
||||
b.Features.Add(new MyCoolNewFeature());
|
||||
});
|
||||
|
||||
// Assert
|
||||
Assert.Single(engine.Engine.Features.OfType<MyCoolNewFeature>());
|
||||
Assert.Single(engine.Engine.Features.OfType<DefaultTagHelperDescriptorProvider>());
|
||||
Assert.Single(engine.Engine.Features.OfType<MvcLatest.ViewComponentTagHelperDescriptorProvider>());
|
||||
Assert.Single(engine.Engine.Features.OfType<MvcLatest.MvcViewDocumentClassifierPass>());
|
||||
Assert.Single(engine.Engine.Features.OfType<MvcLatest.ViewComponentTagHelperPass>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Create_ForUnknownConfiguration_UsesFallbackFactory()
|
||||
{
|
||||
// Arrange
|
||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
||||
projectManager.HostProjectAdded(HostProject_For_UnknownConfiguration);
|
||||
projectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
var snapshot = Snapshot_For_UnknownConfiguration;
|
||||
|
||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager, FallbackFactory, CustomFactories);
|
||||
var factory = new DefaultProjectSnapshotProjectEngineFactory(FallbackFactory, CustomFactories);
|
||||
|
||||
// Act
|
||||
var engine = factoryService.Create("/TestPath/SomePath/", b =>
|
||||
var engine = factory.Create(snapshot, b =>
|
||||
{
|
||||
b.Features.Add(new MyCoolNewFeature());
|
||||
});
|
||||
|
|
@ -221,18 +202,5 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
{
|
||||
public RazorEngine Engine { get; set; }
|
||||
}
|
||||
|
||||
private class TestProjectSnapshotManager : DefaultProjectSnapshotManager
|
||||
{
|
||||
public TestProjectSnapshotManager(Workspace workspace)
|
||||
: base(
|
||||
Mock.Of<ForegroundDispatcher>(),
|
||||
Mock.Of<ErrorReporter>(),
|
||||
Mock.Of<ProjectSnapshotWorker>(),
|
||||
Enumerable.Empty<ProjectSnapshotChangeTrigger>(),
|
||||
workspace)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,8 +3,10 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.Editor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
|
@ -18,29 +20,90 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
{
|
||||
public class DefaultVisualStudioDocumentTrackerTest : ForegroundDispatcherTestBase
|
||||
{
|
||||
private IContentType RazorCoreContentType { get; } = Mock.Of<IContentType>(c => c.IsOfType(RazorLanguage.CoreContentType) == true);
|
||||
public DefaultVisualStudioDocumentTrackerTest()
|
||||
{
|
||||
RazorCoreContentType = Mock.Of<IContentType>(c => c.IsOfType(RazorLanguage.ContentType) == true);
|
||||
TextBuffer = Mock.Of<ITextBuffer>(b => b.ContentType == RazorCoreContentType);
|
||||
|
||||
private ITextBuffer TextBuffer => Mock.Of<ITextBuffer>(b => b.ContentType == RazorCoreContentType);
|
||||
FilePath = "C:/Some/Path/TestDocumentTracker.cshtml";
|
||||
ProjectPath = "C:/Some/Path/TestProject.csproj";
|
||||
|
||||
private string FilePath => "C:/Some/Path/TestDocumentTracker.cshtml";
|
||||
ImportDocumentManager = Mock.Of<ImportDocumentManager>();
|
||||
WorkspaceEditorSettings = new DefaultWorkspaceEditorSettings(Mock.Of<ForegroundDispatcher>(), Mock.Of<EditorSettingsManager>());
|
||||
|
||||
private string ProjectPath => "C:/Some/Path/TestProject.csproj";
|
||||
TagHelperResolver = new TestTagHelperResolver();
|
||||
SomeTagHelpers = new List<TagHelperDescriptor>()
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("test", "test").Build(),
|
||||
};
|
||||
|
||||
private ProjectSnapshotManager ProjectManager => Mock.Of<ProjectSnapshotManager>(p => p.Projects == new List<ProjectSnapshot>());
|
||||
HostServices = TestServices.Create(
|
||||
new IWorkspaceService[] { },
|
||||
new ILanguageService[] { TagHelperResolver, });
|
||||
|
||||
private WorkspaceEditorSettings WorkspaceEditorSettings => new DefaultWorkspaceEditorSettings(Dispatcher, Mock.Of<EditorSettingsManager>());
|
||||
Workspace = TestWorkspace.Create(HostServices, w =>
|
||||
{
|
||||
WorkspaceProject = w.AddProject(ProjectInfo.Create(
|
||||
ProjectId.CreateNewId(),
|
||||
new VersionStamp(),
|
||||
"Test1",
|
||||
"TestAssembly",
|
||||
LanguageNames.CSharp,
|
||||
filePath: ProjectPath));
|
||||
});
|
||||
|
||||
private Workspace Workspace => TestWorkspace.Create();
|
||||
ProjectManager = new TestProjectSnapshotManager(Dispatcher, Workspace) { AllowNotifyListeners = true };
|
||||
|
||||
private ImportDocumentManager ImportDocumentManager => Mock.Of<ImportDocumentManager>();
|
||||
HostProject = new HostProject(ProjectPath, FallbackRazorConfiguration.MVC_2_1);
|
||||
OtherHostProject = new HostProject(ProjectPath, FallbackRazorConfiguration.MVC_2_0);
|
||||
|
||||
[Fact]
|
||||
DocumentTracker = new DefaultVisualStudioDocumentTracker(
|
||||
Dispatcher,
|
||||
FilePath,
|
||||
ProjectPath,
|
||||
ProjectManager,
|
||||
WorkspaceEditorSettings,
|
||||
Workspace,
|
||||
TextBuffer,
|
||||
ImportDocumentManager);
|
||||
}
|
||||
|
||||
private IContentType RazorCoreContentType { get; }
|
||||
|
||||
private ITextBuffer TextBuffer { get; }
|
||||
|
||||
private string FilePath { get; }
|
||||
|
||||
private string ProjectPath { get; }
|
||||
|
||||
private HostProject HostProject { get; }
|
||||
|
||||
private HostProject OtherHostProject { get; }
|
||||
|
||||
private Project WorkspaceProject { get; set; }
|
||||
|
||||
private ImportDocumentManager ImportDocumentManager { get; }
|
||||
|
||||
private WorkspaceEditorSettings WorkspaceEditorSettings { get; }
|
||||
|
||||
private List<TagHelperDescriptor> SomeTagHelpers { get; }
|
||||
|
||||
private TestTagHelperResolver TagHelperResolver { get; }
|
||||
|
||||
private ProjectSnapshotManagerBase ProjectManager { get; }
|
||||
|
||||
private HostServices HostServices { get; }
|
||||
|
||||
private Workspace Workspace { get; }
|
||||
|
||||
private DefaultVisualStudioDocumentTracker DocumentTracker { get; }
|
||||
|
||||
[ForegroundFact]
|
||||
public void EditorSettingsManager_Changed_TriggersContextChanged()
|
||||
{
|
||||
// Arrange
|
||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager);
|
||||
var called = false;
|
||||
documentTracker.ContextChanged += (sender, args) =>
|
||||
DocumentTracker.ContextChanged += (sender, args) =>
|
||||
{
|
||||
Assert.Equal(ContextChangeKind.EditorSettingsChanged, args.Kind);
|
||||
called = true;
|
||||
|
|
@ -48,103 +111,109 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
};
|
||||
|
||||
// Act
|
||||
documentTracker.EditorSettingsManager_Changed(null, null);
|
||||
DocumentTracker.EditorSettingsManager_Changed(null, null);
|
||||
|
||||
// Assert
|
||||
Assert.True(called);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectManager_Changed_ProjectChanged_TriggersContextChanged()
|
||||
[ForegroundFact]
|
||||
public void ProjectManager_Changed_ProjectAdded_TriggersContextChanged()
|
||||
{
|
||||
// Arrange
|
||||
Project project = null;
|
||||
var workspace = TestWorkspace.Create(ws =>
|
||||
{
|
||||
project = ws.AddProject(ProjectInfo.Create(ProjectId.CreateNewId(), new VersionStamp(), "Test1", "TestAssembly", LanguageNames.CSharp, filePath: "C:/Some/Path/TestProject.csproj"));
|
||||
});
|
||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, workspace, TextBuffer, ImportDocumentManager);
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
|
||||
var projectSnapshot = new DefaultProjectSnapshot(new HostProject(project.FilePath, RazorConfiguration.Default), project);
|
||||
var projectChangedArgs = new ProjectChangeEventArgs(projectSnapshot, ProjectChangeKind.Changed);
|
||||
var e = new ProjectChangeEventArgs(ProjectPath, ProjectChangeKind.ProjectAdded);
|
||||
|
||||
var called = false;
|
||||
documentTracker.ContextChanged += (sender, args) =>
|
||||
DocumentTracker.ContextChanged += (sender, args) =>
|
||||
{
|
||||
Assert.Equal(ContextChangeKind.ProjectChanged, args.Kind);
|
||||
called = true;
|
||||
|
||||
Assert.Same(ProjectManager.GetLoadedProject(DocumentTracker.ProjectPath), DocumentTracker.ProjectSnapshot);
|
||||
};
|
||||
|
||||
// Act
|
||||
documentTracker.ProjectManager_Changed(null, projectChangedArgs);
|
||||
DocumentTracker.ProjectManager_Changed(ProjectManager, e);
|
||||
|
||||
// Assert
|
||||
Assert.True(called);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectManager_Changed_TagHelpersChanged_TriggersContextChanged()
|
||||
[ForegroundFact]
|
||||
public void ProjectManager_Changed_ProjectChanged_TriggersContextChanged()
|
||||
{
|
||||
// Arrange
|
||||
Project project = null;
|
||||
var workspace = TestWorkspace.Create(ws =>
|
||||
{
|
||||
project = ws.AddProject(ProjectInfo.Create(ProjectId.CreateNewId(), new VersionStamp(), "Test1", "TestAssembly", LanguageNames.CSharp, filePath: "C:/Some/Path/TestProject.csproj"));
|
||||
});
|
||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, workspace, TextBuffer, ImportDocumentManager);
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
|
||||
var projectSnapshot = new DefaultProjectSnapshot(new HostProject(project.FilePath, RazorConfiguration.Default), project);
|
||||
var projectChangedArgs = new ProjectChangeEventArgs(projectSnapshot, ProjectChangeKind.TagHelpersChanged);
|
||||
var e = new ProjectChangeEventArgs(ProjectPath, ProjectChangeKind.ProjectChanged);
|
||||
|
||||
var called = false;
|
||||
documentTracker.ContextChanged += (sender, args) =>
|
||||
DocumentTracker.ContextChanged += (sender, args) =>
|
||||
{
|
||||
Assert.Equal(ContextChangeKind.TagHelpersChanged, args.Kind);
|
||||
Assert.Equal(ContextChangeKind.ProjectChanged, args.Kind);
|
||||
called = true;
|
||||
|
||||
Assert.Same(ProjectManager.GetLoadedProject(DocumentTracker.ProjectPath), DocumentTracker.ProjectSnapshot);
|
||||
};
|
||||
|
||||
// Act
|
||||
documentTracker.ProjectManager_Changed(null, projectChangedArgs);
|
||||
DocumentTracker.ProjectManager_Changed(ProjectManager, e);
|
||||
|
||||
// Assert
|
||||
Assert.True(called);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ForegroundFact]
|
||||
public void ProjectManager_Changed_ProjectRemoved_TriggersContextChanged_WithEphemeralProject()
|
||||
{
|
||||
// Arrange
|
||||
var e = new ProjectChangeEventArgs(ProjectPath, ProjectChangeKind.ProjectRemoved);
|
||||
|
||||
var called = false;
|
||||
DocumentTracker.ContextChanged += (sender, args) =>
|
||||
{
|
||||
// This can be called both with tag helper and project changes.
|
||||
called = true;
|
||||
|
||||
Assert.IsType<EphemeralProjectSnapshot>(DocumentTracker.ProjectSnapshot);
|
||||
};
|
||||
|
||||
// Act
|
||||
DocumentTracker.ProjectManager_Changed(ProjectManager, e);
|
||||
|
||||
// Assert
|
||||
Assert.True(called);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void ProjectManager_Changed_IgnoresUnknownProject()
|
||||
{
|
||||
// Arrange
|
||||
Project project = null;
|
||||
var workspace = TestWorkspace.Create(ws =>
|
||||
{
|
||||
project = ws.AddProject(ProjectInfo.Create(ProjectId.CreateNewId(), new VersionStamp(), "Test1", "TestAssembly", LanguageNames.CSharp, filePath: "C:/Some/Other/Path/TestProject.csproj"));
|
||||
});
|
||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, workspace, TextBuffer, ImportDocumentManager);
|
||||
|
||||
var projectSnapshot = new DefaultProjectSnapshot(new HostProject(project.FilePath, RazorConfiguration.Default), project);
|
||||
var projectChangedArgs = new ProjectChangeEventArgs(projectSnapshot, ProjectChangeKind.Changed);
|
||||
var e = new ProjectChangeEventArgs("c:/OtherPath/OtherProject.csproj", ProjectChangeKind.ProjectChanged);
|
||||
|
||||
var called = false;
|
||||
documentTracker.ContextChanged += (sender, args) =>
|
||||
DocumentTracker.ContextChanged += (sender, args) =>
|
||||
{
|
||||
called = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
documentTracker.ProjectManager_Changed(null, projectChangedArgs);
|
||||
DocumentTracker.ProjectManager_Changed(ProjectManager, e);
|
||||
|
||||
// Assert
|
||||
Assert.False(called);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ForegroundFact]
|
||||
public void Import_Changed_ImportAssociatedWithDocument_TriggersContextChanged()
|
||||
{
|
||||
// Arrange
|
||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager);
|
||||
|
||||
var called = false;
|
||||
documentTracker.ContextChanged += (sender, args) =>
|
||||
DocumentTracker.ContextChanged += (sender, args) =>
|
||||
{
|
||||
Assert.Equal(ContextChangeKind.ImportsChanged, args.Kind);
|
||||
called = true;
|
||||
|
|
@ -153,19 +222,17 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
var importChangedArgs = new ImportChangedEventArgs("path/to/import", FileChangeKind.Changed, new[] { FilePath });
|
||||
|
||||
// Act
|
||||
documentTracker.Import_Changed(null, importChangedArgs);
|
||||
DocumentTracker.Import_Changed(null, importChangedArgs);
|
||||
|
||||
// Assert
|
||||
Assert.True(called);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ForegroundFact]
|
||||
public void Import_Changed_UnrelatedImport_DoesNothing()
|
||||
{
|
||||
// Arrange
|
||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager);
|
||||
|
||||
documentTracker.ContextChanged += (sender, args) =>
|
||||
DocumentTracker.ContextChanged += (sender, args) =>
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
};
|
||||
|
|
@ -173,152 +240,254 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
var importChangedArgs = new ImportChangedEventArgs("path/to/import", FileChangeKind.Changed, new[] { "path/to/differentfile" });
|
||||
|
||||
// Act & Assert (Does not throw)
|
||||
documentTracker.Import_Changed(null, importChangedArgs);
|
||||
DocumentTracker.Import_Changed(null, importChangedArgs);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ForegroundFact]
|
||||
public void Subscribe_SetsSupportedProjectAndTriggersContextChanged()
|
||||
{
|
||||
// Arrange
|
||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager);
|
||||
var called = false;
|
||||
documentTracker.ContextChanged += (sender, args) =>
|
||||
DocumentTracker.ContextChanged += (sender, args) =>
|
||||
{
|
||||
called = true;
|
||||
Assert.Equal(ContextChangeKind.ProjectChanged, args.Kind);
|
||||
called = true; // This will trigger both ContextChanged and TagHelprsChanged
|
||||
};
|
||||
|
||||
// Act
|
||||
documentTracker.Subscribe();
|
||||
DocumentTracker.Subscribe();
|
||||
|
||||
// Assert
|
||||
Assert.True(called);
|
||||
Assert.True(documentTracker.IsSupportedProject);
|
||||
Assert.True(DocumentTracker.IsSupportedProject);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ForegroundFact]
|
||||
public void Unsubscribe_ResetsSupportedProjectAndTriggersContextChanged()
|
||||
{
|
||||
// Arrange
|
||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager);
|
||||
|
||||
// Subscribe once to set supported project
|
||||
documentTracker.Subscribe();
|
||||
DocumentTracker.Subscribe();
|
||||
|
||||
var called = false;
|
||||
documentTracker.ContextChanged += (sender, args) =>
|
||||
DocumentTracker.ContextChanged += (sender, args) =>
|
||||
{
|
||||
called = true;
|
||||
Assert.Equal(ContextChangeKind.ProjectChanged, args.Kind);
|
||||
};
|
||||
|
||||
// Act
|
||||
documentTracker.Unsubscribe();
|
||||
DocumentTracker.Unsubscribe();
|
||||
|
||||
// Assert
|
||||
Assert.False(documentTracker.IsSupportedProject);
|
||||
Assert.False(DocumentTracker.IsSupportedProject);
|
||||
Assert.True(called);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ForegroundFact]
|
||||
public void AddTextView_AddsToTextViewCollection()
|
||||
{
|
||||
// Arrange
|
||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager);
|
||||
var textView = Mock.Of<ITextView>();
|
||||
|
||||
// Act
|
||||
documentTracker.AddTextView(textView);
|
||||
DocumentTracker.AddTextView(textView);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(documentTracker.TextViews, v => Assert.Same(v, textView));
|
||||
Assert.Collection(DocumentTracker.TextViews, v => Assert.Same(v, textView));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ForegroundFact]
|
||||
public void AddTextView_DoesNotAddDuplicateTextViews()
|
||||
{
|
||||
// Arrange
|
||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager);
|
||||
var textView = Mock.Of<ITextView>();
|
||||
|
||||
// Act
|
||||
documentTracker.AddTextView(textView);
|
||||
documentTracker.AddTextView(textView);
|
||||
DocumentTracker.AddTextView(textView);
|
||||
DocumentTracker.AddTextView(textView);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(documentTracker.TextViews, v => Assert.Same(v, textView));
|
||||
Assert.Collection(DocumentTracker.TextViews, v => Assert.Same(v, textView));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ForegroundFact]
|
||||
public void AddTextView_AddsMultipleTextViewsToCollection()
|
||||
{
|
||||
// Arrange
|
||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager);
|
||||
var textView1 = Mock.Of<ITextView>();
|
||||
var textView2 = Mock.Of<ITextView>();
|
||||
|
||||
// Act
|
||||
documentTracker.AddTextView(textView1);
|
||||
documentTracker.AddTextView(textView2);
|
||||
DocumentTracker.AddTextView(textView1);
|
||||
DocumentTracker.AddTextView(textView2);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
documentTracker.TextViews,
|
||||
DocumentTracker.TextViews,
|
||||
v => Assert.Same(v, textView1),
|
||||
v => Assert.Same(v, textView2));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ForegroundFact]
|
||||
public void RemoveTextView_RemovesTextViewFromCollection_SingleItem()
|
||||
{
|
||||
// Arrange
|
||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager);
|
||||
var textView = Mock.Of<ITextView>();
|
||||
documentTracker.AddTextView(textView);
|
||||
DocumentTracker.AddTextView(textView);
|
||||
|
||||
// Act
|
||||
documentTracker.RemoveTextView(textView);
|
||||
DocumentTracker.RemoveTextView(textView);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(documentTracker.TextViews);
|
||||
Assert.Empty(DocumentTracker.TextViews);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ForegroundFact]
|
||||
public void RemoveTextView_RemovesTextViewFromCollection_MultipleItems()
|
||||
{
|
||||
// Arrange
|
||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager);
|
||||
var textView1 = Mock.Of<ITextView>();
|
||||
var textView2 = Mock.Of<ITextView>();
|
||||
var textView3 = Mock.Of<ITextView>();
|
||||
documentTracker.AddTextView(textView1);
|
||||
documentTracker.AddTextView(textView2);
|
||||
documentTracker.AddTextView(textView3);
|
||||
DocumentTracker.AddTextView(textView1);
|
||||
DocumentTracker.AddTextView(textView2);
|
||||
DocumentTracker.AddTextView(textView3);
|
||||
|
||||
// Act
|
||||
documentTracker.RemoveTextView(textView2);
|
||||
DocumentTracker.RemoveTextView(textView2);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
documentTracker.TextViews,
|
||||
DocumentTracker.TextViews,
|
||||
v => Assert.Same(v, textView1),
|
||||
v => Assert.Same(v, textView3));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ForegroundFact]
|
||||
public void RemoveTextView_NoopsWhenRemovingTextViewNotInCollection()
|
||||
{
|
||||
// Arrange
|
||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager);
|
||||
var textView1 = Mock.Of<ITextView>();
|
||||
documentTracker.AddTextView(textView1);
|
||||
DocumentTracker.AddTextView(textView1);
|
||||
var textView2 = Mock.Of<ITextView>();
|
||||
|
||||
// Act
|
||||
documentTracker.RemoveTextView(textView2);
|
||||
DocumentTracker.RemoveTextView(textView2);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(documentTracker.TextViews, v => Assert.Same(v, textView1));
|
||||
Assert.Collection(DocumentTracker.TextViews, v => Assert.Same(v, textView1));
|
||||
}
|
||||
|
||||
|
||||
[ForegroundFact]
|
||||
public void Subscribed_InitializesEphemeralProjectSnapshot()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
DocumentTracker.Subscribe();
|
||||
|
||||
// Assert
|
||||
Assert.IsType<EphemeralProjectSnapshot>(DocumentTracker.ProjectSnapshot);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void Subscribed_InitializesRealProjectSnapshot()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
|
||||
// Act
|
||||
DocumentTracker.Subscribe();
|
||||
|
||||
// Assert
|
||||
Assert.IsType<DefaultProjectSnapshot>(DocumentTracker.ProjectSnapshot);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task Subscribed_ListensToProjectChanges()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
|
||||
DocumentTracker.Subscribe();
|
||||
|
||||
await DocumentTracker.PendingTagHelperTask;
|
||||
|
||||
// There can be multiple args here because the tag helpers will return
|
||||
// immediately and trigger another ContextChanged.
|
||||
List<ContextChangeEventArgs> args = new List<ContextChangeEventArgs>();
|
||||
DocumentTracker.ContextChanged += (sender, e) => { args.Add(e); };
|
||||
|
||||
// Act
|
||||
ProjectManager.HostProjectChanged(OtherHostProject);
|
||||
await DocumentTracker.PendingTagHelperTask;
|
||||
|
||||
// Assert
|
||||
var snapshot = Assert.IsType<DefaultProjectSnapshot>(DocumentTracker.ProjectSnapshot);
|
||||
|
||||
Assert.Same(OtherHostProject, snapshot.HostProject);
|
||||
|
||||
Assert.Collection(
|
||||
args,
|
||||
e => Assert.Equal(ContextChangeKind.ProjectChanged, e.Kind),
|
||||
e => Assert.Equal(ContextChangeKind.TagHelpersChanged, e.Kind));
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task Subscribed_ListensToProjectRemoval()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
|
||||
DocumentTracker.Subscribe();
|
||||
|
||||
await DocumentTracker.PendingTagHelperTask;
|
||||
|
||||
List<ContextChangeEventArgs> args = new List<ContextChangeEventArgs>();
|
||||
DocumentTracker.ContextChanged += (sender, e) => { args.Add(e); };
|
||||
|
||||
// Act
|
||||
ProjectManager.HostProjectRemoved(HostProject);
|
||||
await DocumentTracker.PendingTagHelperTask;
|
||||
|
||||
// Assert
|
||||
Assert.IsType<EphemeralProjectSnapshot>(DocumentTracker.ProjectSnapshot);
|
||||
|
||||
Assert.Collection(
|
||||
args,
|
||||
e => Assert.Equal(ContextChangeKind.ProjectChanged, e.Kind),
|
||||
e => Assert.Equal(ContextChangeKind.TagHelpersChanged, e.Kind));
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task Subscribed_ListensToProjectChanges_ComputesTagHelpers()
|
||||
{
|
||||
// Arrange
|
||||
TagHelperResolver.CompletionSource = new TaskCompletionSource<TagHelperResolutionResult>();
|
||||
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
|
||||
DocumentTracker.Subscribe();
|
||||
|
||||
// We haven't let the tag helpers complete yet
|
||||
Assert.False(DocumentTracker.PendingTagHelperTask.IsCompleted);
|
||||
Assert.Empty(DocumentTracker.TagHelpers);
|
||||
|
||||
List<ContextChangeEventArgs> args = new List<ContextChangeEventArgs>();
|
||||
DocumentTracker.ContextChanged += (sender, e) => { args.Add(e); };
|
||||
|
||||
// Act
|
||||
TagHelperResolver.CompletionSource.SetResult(new TagHelperResolutionResult(SomeTagHelpers, Array.Empty<RazorDiagnostic>()));
|
||||
await DocumentTracker.PendingTagHelperTask;
|
||||
|
||||
// Assert
|
||||
Assert.Same(DocumentTracker.TagHelpers, SomeTagHelpers);
|
||||
|
||||
Assert.Collection(
|
||||
args,
|
||||
e => Assert.Equal(ContextChangeKind.TagHelpersChanged, e.Kind));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,13 +4,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Microsoft.VisualStudio.Test;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
using Microsoft.VisualStudio.Text.Editor;
|
||||
|
|
@ -24,6 +25,16 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
private const string TestLinePragmaFileName = "C:\\This\\Path\\Is\\Just\\For\\Line\\Pragmas.cshtml";
|
||||
private const string TestProjectPath = "C:\\This\\Path\\Is\\Just\\For\\Project.csproj";
|
||||
|
||||
public DefaultVisualStudioRazorParserIntegrationTest()
|
||||
{
|
||||
Workspace = TestWorkspace.Create();
|
||||
ProjectSnapshot = new EphemeralProjectSnapshot(Workspace.Services, TestProjectPath);
|
||||
}
|
||||
|
||||
private ProjectSnapshot ProjectSnapshot { get; }
|
||||
|
||||
private Workspace Workspace { get; }
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task BufferChangeStartsFullReparseIfChangeOverlapsMultipleSpans()
|
||||
{
|
||||
|
|
@ -505,7 +516,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
{
|
||||
var textBuffer = new TestTextBuffer(originalSnapshot);
|
||||
var documentTracker = CreateDocumentTracker(textBuffer);
|
||||
var templateEngineFactory = CreateTemplateEngineFactory();
|
||||
var templateEngineFactory = CreateProjectEngineFactory();
|
||||
var parser = new DefaultVisualStudioRazorParser(
|
||||
Dispatcher,
|
||||
documentTracker,
|
||||
|
|
@ -524,7 +535,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
return new TestParserManager(parser);
|
||||
}
|
||||
|
||||
private static RazorProjectEngineFactoryService CreateTemplateEngineFactory(
|
||||
private static ProjectSnapshotProjectEngineFactory CreateProjectEngineFactory(
|
||||
string path = TestLinePragmaFileName,
|
||||
IEnumerable<TagHelperDescriptor> tagHelpers = null)
|
||||
{
|
||||
|
|
@ -541,10 +552,10 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
}
|
||||
});
|
||||
|
||||
var projectEngineFactoryService = Mock.Of<RazorProjectEngineFactoryService>(
|
||||
service => service.Create(It.IsAny<string>(), It.IsAny<Action<RazorProjectEngineBuilder>>()) == projectEngine);
|
||||
|
||||
return projectEngineFactoryService;
|
||||
return new TestProjectSnapshotProjectEngineFactory()
|
||||
{
|
||||
Engine = projectEngine,
|
||||
};
|
||||
}
|
||||
|
||||
private async Task RunTypeKeywordTestAsync(string keyword)
|
||||
|
|
@ -584,7 +595,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
#endif
|
||||
}
|
||||
|
||||
private static VisualStudioDocumentTracker CreateDocumentTracker(Text.ITextBuffer textBuffer)
|
||||
private VisualStudioDocumentTracker CreateDocumentTracker(Text.ITextBuffer textBuffer)
|
||||
{
|
||||
var focusedTextView = Mock.Of<ITextView>(textView => textView.HasAggregateFocus == true);
|
||||
var documentTracker = Mock.Of<VisualStudioDocumentTracker>(tracker =>
|
||||
|
|
@ -592,6 +603,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
tracker.TextViews == new[] { focusedTextView } &&
|
||||
tracker.FilePath == TestLinePragmaFileName &&
|
||||
tracker.ProjectPath == TestProjectPath &&
|
||||
tracker.ProjectSnapshot == ProjectSnapshot &&
|
||||
tracker.IsSupportedProject == true);
|
||||
textBuffer.Properties.AddProperty(typeof(VisualStudioDocumentTracker), documentTracker);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Microsoft.VisualStudio.Test;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
using Moq;
|
||||
|
|
@ -14,12 +16,32 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
{
|
||||
public class DefaultVisualStudioRazorParserTest : ForegroundDispatcherTestBase
|
||||
{
|
||||
private static VisualStudioDocumentTracker CreateDocumentTracker(bool isSupportedProject = true)
|
||||
public DefaultVisualStudioRazorParserTest()
|
||||
{
|
||||
Workspace = TestWorkspace.Create();
|
||||
ProjectSnapshot = new EphemeralProjectSnapshot(Workspace.Services, "c:\\SomeProject.csproj");
|
||||
|
||||
var engine = RazorProjectEngine.Create(RazorConfiguration.Default, RazorProjectFileSystem.Empty);
|
||||
ProjectEngineFactory = Mock.Of<ProjectSnapshotProjectEngineFactory>(
|
||||
f => f.Create(
|
||||
It.IsAny<ProjectSnapshot>(),
|
||||
It.IsAny<RazorProjectFileSystem>(),
|
||||
It.IsAny<Action<RazorProjectEngineBuilder>>()) == engine);
|
||||
}
|
||||
|
||||
private ProjectSnapshot ProjectSnapshot { get; }
|
||||
|
||||
private ProjectSnapshotProjectEngineFactory ProjectEngineFactory { get; }
|
||||
|
||||
private Workspace Workspace { get; }
|
||||
|
||||
private VisualStudioDocumentTracker CreateDocumentTracker(bool isSupportedProject = true)
|
||||
{
|
||||
var documentTracker = Mock.Of<VisualStudioDocumentTracker>(tracker =>
|
||||
tracker.TextBuffer == new TestTextBuffer(new StringTextSnapshot(string.Empty)) &&
|
||||
tracker.ProjectPath == "SomeProject.csproj" &&
|
||||
tracker.FilePath == "SomeFilePath.cshtml" &&
|
||||
tracker.ProjectPath == "c:\\SomeProject.csproj" &&
|
||||
tracker.ProjectSnapshot == ProjectSnapshot &&
|
||||
tracker.FilePath == "c:\\SomeFilePath.cshtml" &&
|
||||
tracker.IsSupportedProject == isSupportedProject);
|
||||
|
||||
return documentTracker;
|
||||
|
|
@ -32,7 +54,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
var parser = new DefaultVisualStudioRazorParser(
|
||||
Dispatcher,
|
||||
CreateDocumentTracker(),
|
||||
Mock.Of<RazorProjectEngineFactoryService>(),
|
||||
ProjectEngineFactory,
|
||||
new DefaultErrorReporter(),
|
||||
Mock.Of<VisualStudioCompletionBroker>());
|
||||
parser.Dispose();
|
||||
|
|
@ -48,7 +70,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
var parser = new DefaultVisualStudioRazorParser(
|
||||
Dispatcher,
|
||||
CreateDocumentTracker(),
|
||||
Mock.Of<RazorProjectEngineFactoryService>(),
|
||||
ProjectEngineFactory,
|
||||
new DefaultErrorReporter(),
|
||||
Mock.Of<VisualStudioCompletionBroker>());
|
||||
parser.Dispose();
|
||||
|
|
@ -64,7 +86,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
var parser = new DefaultVisualStudioRazorParser(
|
||||
Dispatcher,
|
||||
CreateDocumentTracker(),
|
||||
Mock.Of<RazorProjectEngineFactoryService>(),
|
||||
ProjectEngineFactory,
|
||||
new DefaultErrorReporter(),
|
||||
Mock.Of<VisualStudioCompletionBroker>());
|
||||
parser.Dispose();
|
||||
|
|
@ -80,7 +102,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
using (var parser = new DefaultVisualStudioRazorParser(
|
||||
Dispatcher,
|
||||
CreateDocumentTracker(),
|
||||
Mock.Of<RazorProjectEngineFactoryService>(),
|
||||
ProjectEngineFactory,
|
||||
new DefaultErrorReporter(),
|
||||
Mock.Of<VisualStudioCompletionBroker>()))
|
||||
{
|
||||
|
|
@ -108,7 +130,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
using (var parser = new DefaultVisualStudioRazorParser(
|
||||
Dispatcher,
|
||||
documentTracker,
|
||||
Mock.Of<RazorProjectEngineFactoryService>(),
|
||||
ProjectEngineFactory,
|
||||
new DefaultErrorReporter(),
|
||||
Mock.Of<VisualStudioCompletionBroker>()))
|
||||
{
|
||||
|
|
@ -139,7 +161,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
using (var parser = new DefaultVisualStudioRazorParser(
|
||||
Dispatcher,
|
||||
CreateDocumentTracker(),
|
||||
Mock.Of<RazorProjectEngineFactoryService>(),
|
||||
ProjectEngineFactory,
|
||||
new DefaultErrorReporter(),
|
||||
Mock.Of<VisualStudioCompletionBroker>())
|
||||
{
|
||||
|
|
@ -169,7 +191,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
using (var parser = new DefaultVisualStudioRazorParser(
|
||||
Dispatcher,
|
||||
CreateDocumentTracker(),
|
||||
Mock.Of<RazorProjectEngineFactoryService>(),
|
||||
ProjectEngineFactory,
|
||||
new DefaultErrorReporter(),
|
||||
Mock.Of<VisualStudioCompletionBroker>())
|
||||
{
|
||||
|
|
@ -198,7 +220,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
using (var parser = new DefaultVisualStudioRazorParser(
|
||||
Dispatcher,
|
||||
CreateDocumentTracker(),
|
||||
Mock.Of<RazorProjectEngineFactoryService>(),
|
||||
ProjectEngineFactory,
|
||||
new DefaultErrorReporter(),
|
||||
Mock.Of<VisualStudioCompletionBroker>()))
|
||||
{
|
||||
|
|
@ -222,7 +244,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
using (var parser = new DefaultVisualStudioRazorParser(
|
||||
Dispatcher,
|
||||
documentTracker,
|
||||
Mock.Of<RazorProjectEngineFactoryService>(),
|
||||
ProjectEngineFactory,
|
||||
new DefaultErrorReporter(),
|
||||
Mock.Of<VisualStudioCompletionBroker>()))
|
||||
{
|
||||
|
|
@ -242,7 +264,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
using (var parser = new DefaultVisualStudioRazorParser(
|
||||
Dispatcher,
|
||||
CreateDocumentTracker(isSupportedProject: true),
|
||||
Mock.Of<RazorProjectEngineFactoryService>(),
|
||||
ProjectEngineFactory,
|
||||
new DefaultErrorReporter(),
|
||||
Mock.Of<VisualStudioCompletionBroker>()))
|
||||
{
|
||||
|
|
@ -261,7 +283,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
using (var parser = new DefaultVisualStudioRazorParser(
|
||||
Dispatcher,
|
||||
CreateDocumentTracker(isSupportedProject: false),
|
||||
Mock.Of<RazorProjectEngineFactoryService>(),
|
||||
ProjectEngineFactory,
|
||||
new DefaultErrorReporter(),
|
||||
Mock.Of<VisualStudioCompletionBroker>()))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,6 +5,12 @@
|
|||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Microsoft.CodeAnalysis.Razor.Workspaces.Test\Shared\**\*.cs">
|
||||
<Link>Shared\%(RecursiveDir)%(FileName)%(Extension)</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -12,12 +12,18 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
// These tests are really integration tests. There isn't a good way to unit test this functionality since
|
||||
// the only thing in here is threading.
|
||||
public class ProjectSnapshotWorkerQueueTest : ForegroundDispatcherTestBase
|
||||
public class BackgroundDocumentGeneratorTest : ForegroundDispatcherTestBase
|
||||
{
|
||||
public ProjectSnapshotWorkerQueueTest()
|
||||
public BackgroundDocumentGeneratorTest()
|
||||
{
|
||||
HostProject1 = new HostProject("Test1.csproj", FallbackRazorConfiguration.MVC_1_0);
|
||||
HostProject2 = new HostProject("Test2.csproj", FallbackRazorConfiguration.MVC_1_0);
|
||||
Documents = new HostDocument[]
|
||||
{
|
||||
new HostDocument("c:\\Test1\\Index.cshtml", "Index.cshtml"),
|
||||
new HostDocument("c:\\Test1\\Components\\Counter.cshtml", "Components\\Counter.cshtml"),
|
||||
};
|
||||
|
||||
HostProject1 = new HostProject("c:\\Test1\\Test1.csproj", FallbackRazorConfiguration.MVC_1_0);
|
||||
HostProject2 = new HostProject("c:\\Test2\\Test2.csproj", FallbackRazorConfiguration.MVC_1_0);
|
||||
|
||||
Workspace = TestWorkspace.Create();
|
||||
|
||||
|
|
@ -31,19 +37,21 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
"Test1",
|
||||
"Test1",
|
||||
LanguageNames.CSharp,
|
||||
"Test1.csproj"))
|
||||
"c:\\Test1\\Test1.csproj"))
|
||||
.AddProject(ProjectInfo.Create(
|
||||
projectId2,
|
||||
VersionStamp.Default,
|
||||
"Test2",
|
||||
"Test2",
|
||||
LanguageNames.CSharp,
|
||||
"Test2.csproj")); ;
|
||||
"c:\\Test2\\Test2.csproj")); ;
|
||||
|
||||
WorkspaceProject1 = solution.GetProject(projectId1);
|
||||
WorkspaceProject2 = solution.GetProject(projectId2);
|
||||
}
|
||||
|
||||
private HostDocument[] Documents { get; }
|
||||
|
||||
private HostProject HostProject1 { get; }
|
||||
|
||||
private HostProject HostProject2 { get; }
|
||||
|
|
@ -63,28 +71,31 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
projectManager.HostProjectAdded(HostProject2);
|
||||
projectManager.WorkspaceProjectAdded(WorkspaceProject1);
|
||||
projectManager.WorkspaceProjectAdded(WorkspaceProject2);
|
||||
projectManager.DocumentAdded(HostProject1, Documents[0]);
|
||||
projectManager.DocumentAdded(HostProject1, Documents[1]);
|
||||
|
||||
var projectWorker = new TestProjectSnapshotWorker();
|
||||
var project = projectManager.GetLoadedProject(HostProject1.FilePath);
|
||||
|
||||
var queue = new ProjectSnapshotWorkerQueue(Dispatcher, projectManager, projectWorker)
|
||||
var queue = new BackgroundDocumentGenerator(Dispatcher)
|
||||
{
|
||||
Delay = TimeSpan.FromMilliseconds(1),
|
||||
BlockBackgroundWorkStart = new ManualResetEventSlim(initialState: false),
|
||||
NotifyBackgroundWorkFinish = new ManualResetEventSlim(initialState: false),
|
||||
NotifyForegroundWorkFinish = new ManualResetEventSlim(initialState: false),
|
||||
NotifyBackgroundWorkStarting = new ManualResetEventSlim(initialState: false),
|
||||
BlockBackgroundWorkCompleting = new ManualResetEventSlim(initialState: false),
|
||||
NotifyBackgroundWorkCompleted = new ManualResetEventSlim(initialState: false),
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
queue.Enqueue(projectManager.GetSnapshot(HostProject1).CreateUpdateContext());
|
||||
queue.Enqueue(project, project.GetDocument(Documents[0].FilePath));
|
||||
|
||||
Assert.True(queue.IsScheduledOrRunning, "Queue should be scheduled during Enqueue");
|
||||
Assert.True(queue.HasPendingNotifications, "Queue should have a notification created during Enqueue");
|
||||
|
||||
// Allow the background work to proceed.
|
||||
queue.BlockBackgroundWorkStart.Set();
|
||||
queue.BlockBackgroundWorkCompleting.Set();
|
||||
|
||||
// Get off the foreground thread and allow the updates to flow through.
|
||||
await Task.Run(() => queue.NotifyForegroundWorkFinish.Wait(TimeSpan.FromSeconds(1)));
|
||||
await Task.Run(() => queue.NotifyBackgroundWorkCompleted.Wait(TimeSpan.FromSeconds(1)));
|
||||
|
||||
Assert.False(queue.IsScheduledOrRunning, "Queue should not have restarted");
|
||||
Assert.False(queue.HasPendingNotifications, "Queue should have processed all notifications");
|
||||
|
|
@ -99,40 +110,42 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
projectManager.HostProjectAdded(HostProject2);
|
||||
projectManager.WorkspaceProjectAdded(WorkspaceProject1);
|
||||
projectManager.WorkspaceProjectAdded(WorkspaceProject2);
|
||||
projectManager.DocumentAdded(HostProject1, Documents[0]);
|
||||
projectManager.DocumentAdded(HostProject1, Documents[1]);
|
||||
|
||||
var projectWorker = new TestProjectSnapshotWorker();
|
||||
var project = projectManager.GetLoadedProject(HostProject1.FilePath);
|
||||
|
||||
var queue = new ProjectSnapshotWorkerQueue(Dispatcher, projectManager, projectWorker)
|
||||
var queue = new BackgroundDocumentGenerator(Dispatcher)
|
||||
{
|
||||
Delay = TimeSpan.FromMilliseconds(1),
|
||||
BlockBackgroundWorkStart = new ManualResetEventSlim(initialState: false),
|
||||
NotifyBackgroundWorkFinish = new ManualResetEventSlim(initialState: false),
|
||||
NotifyForegroundWorkFinish = new ManualResetEventSlim(initialState: false),
|
||||
NotifyBackgroundWorkStarting = new ManualResetEventSlim(initialState: false),
|
||||
BlockBackgroundWorkCompleting = new ManualResetEventSlim(initialState: false),
|
||||
NotifyBackgroundWorkCompleted = new ManualResetEventSlim(initialState: false),
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
queue.Enqueue(projectManager.GetSnapshot(HostProject1).CreateUpdateContext());
|
||||
queue.Enqueue(project, project.GetDocument(Documents[0].FilePath));
|
||||
|
||||
Assert.True(queue.IsScheduledOrRunning, "Queue should be scheduled during Enqueue");
|
||||
Assert.True(queue.HasPendingNotifications, "Queue should have a notification created during Enqueue");
|
||||
|
||||
// Allow the background work to proceed.
|
||||
// Allow the background work to start.
|
||||
queue.BlockBackgroundWorkStart.Set();
|
||||
|
||||
queue.NotifyBackgroundWorkFinish.Wait(); // Block the foreground thread so we can queue another notification.
|
||||
await Task.Run(() => queue.NotifyBackgroundWorkStarting.Wait(TimeSpan.FromSeconds(1)));
|
||||
|
||||
Assert.True(queue.IsScheduledOrRunning, "Worker should be processing now");
|
||||
Assert.False(queue.HasPendingNotifications, "Worker should have taken all notifications");
|
||||
|
||||
queue.Enqueue(projectManager.GetSnapshot(HostProject2).CreateUpdateContext());
|
||||
|
||||
queue.Enqueue(project, project.GetDocument(Documents[1].FilePath));
|
||||
Assert.True(queue.HasPendingNotifications); // Now we should see the worker restart when it finishes.
|
||||
|
||||
// Get off the foreground thread and allow the updates to flow through.
|
||||
await Task.Run(() => queue.NotifyForegroundWorkFinish.Wait(TimeSpan.FromSeconds(1)));
|
||||
// Allow work to complete, which should restart the timer.
|
||||
queue.BlockBackgroundWorkCompleting.Set();
|
||||
|
||||
queue.NotifyBackgroundWorkFinish.Reset();
|
||||
queue.NotifyForegroundWorkFinish.Reset();
|
||||
await Task.Run(() => queue.NotifyBackgroundWorkCompleted.Wait(TimeSpan.FromSeconds(1)));
|
||||
queue.NotifyBackgroundWorkCompleted.Reset();
|
||||
|
||||
// It should start running again right away.
|
||||
Assert.True(queue.IsScheduledOrRunning, "Queue should be scheduled during Enqueue");
|
||||
|
|
@ -141,51 +154,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// Allow the background work to proceed.
|
||||
queue.BlockBackgroundWorkStart.Set();
|
||||
|
||||
// Get off the foreground thread and allow the updates to flow through.
|
||||
await Task.Run(() => queue.NotifyForegroundWorkFinish.Wait(TimeSpan.FromSeconds(1)));
|
||||
queue.BlockBackgroundWorkCompleting.Set();
|
||||
await Task.Run(() => queue.NotifyBackgroundWorkCompleted.Wait(TimeSpan.FromSeconds(1)));
|
||||
|
||||
Assert.False(queue.IsScheduledOrRunning, "Queue should not have restarted");
|
||||
Assert.False(queue.HasPendingNotifications, "Queue should have processed all notifications");
|
||||
}
|
||||
|
||||
private class TestProjectSnapshotManager : DefaultProjectSnapshotManager
|
||||
{
|
||||
public TestProjectSnapshotManager(ForegroundDispatcher foregroundDispatcher, Workspace workspace)
|
||||
: base(foregroundDispatcher, Mock.Of<ErrorReporter>(), new TestProjectSnapshotWorker(), Enumerable.Empty<ProjectSnapshotChangeTrigger>(), workspace)
|
||||
{
|
||||
}
|
||||
|
||||
public DefaultProjectSnapshot GetSnapshot(HostProject hostProject)
|
||||
{
|
||||
return Projects.Cast<DefaultProjectSnapshot>().FirstOrDefault(s => s.FilePath == hostProject.FilePath);
|
||||
}
|
||||
|
||||
public DefaultProjectSnapshot GetSnapshot(Project workspaceProject)
|
||||
{
|
||||
return Projects.Cast<DefaultProjectSnapshot>().FirstOrDefault(s => s.FilePath == workspaceProject.FilePath);
|
||||
}
|
||||
|
||||
protected override void NotifyListeners(ProjectChangeEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void NotifyBackgroundWorker(ProjectSnapshotUpdateContext context)
|
||||
{
|
||||
Assert.NotNull(context.HostProject);
|
||||
Assert.NotNull(context.WorkspaceProject);
|
||||
}
|
||||
}
|
||||
|
||||
private class TestProjectSnapshotWorker : ProjectSnapshotWorker
|
||||
{
|
||||
public TestProjectSnapshotWorker()
|
||||
{
|
||||
}
|
||||
|
||||
public override Task ProcessUpdateAsync(ProjectSnapshotUpdateContext update, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,12 @@
|
|||
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Microsoft.CodeAnalysis.Razor.Workspaces.Test\Shared\**\*.cs">
|
||||
<Link>Shared\%(RecursiveDir)%(FileName)%(Extension)</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -45,12 +45,12 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
|
||||
ErrorReporter = new DefaultErrorReporter();
|
||||
ProjectManager = new TestProjectSnapshotManager(Workspace);
|
||||
EngineFactory = new DefaultProjectEngineFactoryService(ProjectManager, FallbackFactory, CustomFactories);
|
||||
EngineFactory = new DefaultProjectSnapshotProjectEngineFactory(FallbackFactory, CustomFactories);
|
||||
}
|
||||
|
||||
private ErrorReporter ErrorReporter { get; }
|
||||
|
||||
private RazorProjectEngineFactoryService EngineFactory { get; }
|
||||
private ProjectSnapshotProjectEngineFactory EngineFactory { get; }
|
||||
|
||||
private Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[] CustomFactories { get; }
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject_For_2_0);
|
||||
|
||||
var project = ProjectManager.GetProjectWithFilePath("Test.csproj");
|
||||
var project = ProjectManager.GetLoadedProject("Test.csproj");
|
||||
|
||||
var resolver = new TestTagHelperResolver(EngineFactory, ErrorReporter, Workspace);
|
||||
|
||||
|
|
@ -89,7 +89,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
ProjectManager.HostProjectAdded(HostProject_For_2_0);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
|
||||
var project = ProjectManager.GetProjectWithFilePath("Test.csproj");
|
||||
var project = ProjectManager.GetLoadedProject("Test.csproj");
|
||||
|
||||
var resolver = new TestTagHelperResolver(EngineFactory, ErrorReporter, Workspace)
|
||||
{
|
||||
|
|
@ -105,7 +105,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
var result = await resolver.GetTagHelpersAsync(project);
|
||||
|
||||
// Assert
|
||||
Assert.Same(TagHelperResolutionResult.Empty, result);
|
||||
Assert.Same(TagHelperResolutionResult.Empty, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -115,7 +115,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
ProjectManager.HostProjectAdded(HostProject_For_NonSerializableConfiguration);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
|
||||
var project = ProjectManager.GetProjectWithFilePath("Test.csproj");
|
||||
var project = ProjectManager.GetLoadedProject("Test.csproj");
|
||||
|
||||
var resolver = new TestTagHelperResolver(EngineFactory, ErrorReporter, Workspace)
|
||||
{
|
||||
|
|
@ -136,8 +136,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
|
||||
private class TestTagHelperResolver : OOPTagHelperResolver
|
||||
{
|
||||
public TestTagHelperResolver(RazorProjectEngineFactoryService engineFactory, ErrorReporter errorReporter, Workspace workspace)
|
||||
: base(engineFactory, errorReporter, workspace)
|
||||
public TestTagHelperResolver(ProjectSnapshotProjectEngineFactory factory, ErrorReporter errorReporter, Workspace workspace)
|
||||
: base(factory, errorReporter, workspace)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -163,15 +163,10 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
: base(
|
||||
Mock.Of<ForegroundDispatcher>(),
|
||||
Mock.Of<ErrorReporter>(),
|
||||
Mock.Of<ProjectSnapshotWorker>(),
|
||||
Enumerable.Empty<ProjectSnapshotChangeTrigger>(),
|
||||
workspace)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void NotifyBackgroundWorker(ProjectSnapshotUpdateContext context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -12,9 +16,31 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
public DefaultProjectSnapshotManagerTest()
|
||||
{
|
||||
HostProject = new HostProject("Test.csproj", FallbackRazorConfiguration.MVC_2_0);
|
||||
TagHelperResolver = new TestTagHelperResolver();
|
||||
|
||||
Workspace = TestWorkspace.Create();
|
||||
HostServices = TestServices.Create(
|
||||
new IWorkspaceService[]
|
||||
{
|
||||
new TestProjectSnapshotProjectEngineFactory(),
|
||||
},
|
||||
new ILanguageService[]
|
||||
{
|
||||
TagHelperResolver,
|
||||
});
|
||||
|
||||
Documents = new HostDocument[]
|
||||
{
|
||||
new HostDocument("c:\\MyProject\\File.cshtml", "File.cshtml"),
|
||||
new HostDocument("c:\\MyProject\\Index.cshtml", "Index.cshtml"),
|
||||
|
||||
// linked file
|
||||
new HostDocument("c:\\SomeOtherProject\\Index.cshtml", "Pages\\Index.cshtml"),
|
||||
};
|
||||
|
||||
HostProject = new HostProject("c:\\MyProject\\Test.csproj", FallbackRazorConfiguration.MVC_2_0);
|
||||
HostProjectWithConfigurationChange = new HostProject("c:\\MyProject\\Test.csproj", FallbackRazorConfiguration.MVC_1_0);
|
||||
|
||||
Workspace = TestWorkspace.Create(HostServices);
|
||||
ProjectManager = new TestProjectSnapshotManager(Dispatcher, Enumerable.Empty<ProjectSnapshotChangeTrigger>(), Workspace);
|
||||
|
||||
var projectId = ProjectId.CreateNewId("Test");
|
||||
|
|
@ -24,7 +50,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
"Test",
|
||||
"Test",
|
||||
LanguageNames.CSharp,
|
||||
"Test.csproj"));
|
||||
"c:\\MyProject\\Test.csproj"));
|
||||
WorkspaceProject = solution.GetProject(projectId);
|
||||
|
||||
var vbProjectId = ProjectId.CreateNewId("VB");
|
||||
|
|
@ -54,12 +80,19 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
"Test (Different TFM)",
|
||||
"Test",
|
||||
LanguageNames.CSharp,
|
||||
"Test.csproj"));
|
||||
"c:\\MyProject\\Test.csproj"));
|
||||
WorkspaceProjectWithDifferentTfm = solution.GetProject(projectIdWithDifferentTfm);
|
||||
|
||||
SomeTagHelpers = TagHelperResolver.TagHelpers;
|
||||
SomeTagHelpers.Add(TagHelperDescriptorBuilder.Create("Test1", "TestAssembly").Build());
|
||||
}
|
||||
|
||||
private HostDocument[] Documents { get; }
|
||||
|
||||
private HostProject HostProject { get; }
|
||||
|
||||
private HostProject HostProjectWithConfigurationChange { get; }
|
||||
|
||||
private Project WorkspaceProject { get; }
|
||||
|
||||
private Project WorkspaceProjectWithDifferentTfm { get; }
|
||||
|
|
@ -68,30 +101,203 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
private Project VBWorkspaceProject { get; }
|
||||
|
||||
private TestTagHelperResolver TagHelperResolver { get; }
|
||||
|
||||
private TestProjectSnapshotManager ProjectManager { get; }
|
||||
|
||||
private HostServices HostServices { get; }
|
||||
|
||||
private Workspace Workspace { get; }
|
||||
|
||||
private IList<TagHelperDescriptor> SomeTagHelpers { get; }
|
||||
|
||||
[ForegroundFact]
|
||||
public void HostProjectBuildComplete_FindsChangedWorkspaceProject_AndStartsBackgroundWorker()
|
||||
public void DocumentAdded_AddsDocument()
|
||||
{
|
||||
// Arrange
|
||||
Assert.True(Workspace.TryApplyChanges(WorkspaceProject.Solution));
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
var project = WorkspaceProject.WithAssemblyName("Test1"); // Simulate a project change
|
||||
ProjectManager.WorkspaceProjectAdded(project);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.HostProjectBuildComplete(HostProject);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0]);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
Assert.True(snapshot.IsInitialized);
|
||||
Assert.Collection(snapshot.DocumentFilePaths, d => Assert.Equal(Documents[0].FilePath, d));
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.True(ProjectManager.WorkerStarted);
|
||||
Assert.Equal(ProjectChangeKind.DocumentsChanged, ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void DocumentAdded_IgnoresDuplicate()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0]);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0]);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
Assert.Collection(snapshot.DocumentFilePaths, d => Assert.Equal(Documents[0].FilePath, d));
|
||||
|
||||
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void DocumentAdded_IgnoresUnknownProject()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0]);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
Assert.Null(snapshot);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task DocumentAdded_CachesTagHelpers()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
|
||||
// Adding some computed state
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
await snapshot.GetTagHelpersAsync();
|
||||
|
||||
// Act
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0]);
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
Assert.True(snapshot.TryGetTagHelpers(out var _));
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void DocumentAdded_CachesProjectEngine()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
var projectEngine = snapshot.GetProjectEngine();
|
||||
|
||||
// Act
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0]);
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
Assert.Same(projectEngine, snapshot.GetProjectEngine());
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void DocumentRemoved_RemovesDocument()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0]);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[1]);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[2]);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.DocumentRemoved(HostProject, Documents[1]);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
Assert.Collection(
|
||||
snapshot.DocumentFilePaths,
|
||||
d => Assert.Equal(Documents[0].FilePath, d),
|
||||
d => Assert.Equal(Documents[2].FilePath, d));
|
||||
|
||||
Assert.Equal(ProjectChangeKind.DocumentsChanged, ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void DocumentRemoved_IgnoresNotFoundDocument()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.DocumentRemoved(HostProject, Documents[0]);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
Assert.Empty(snapshot.DocumentFilePaths);
|
||||
|
||||
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void DocumentRemoved_IgnoresUnknownProject()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
ProjectManager.DocumentRemoved(HostProject, Documents[0]);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
Assert.Null(snapshot);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task DocumentRemoved_CachesTagHelpers()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0]);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[1]);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[2]);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Adding some computed state
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
await snapshot.GetTagHelpersAsync();
|
||||
|
||||
// Act
|
||||
ProjectManager.DocumentRemoved(HostProject, Documents[1]);
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
Assert.True(snapshot.TryGetTagHelpers(out var _));
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void DocumentRemoved_CachesProjectEngine()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0]);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[1]);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[2]);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
var projectEngine = snapshot.GetProjectEngine();
|
||||
|
||||
// Act
|
||||
ProjectManager.DocumentRemoved(HostProject, Documents[1]);
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
Assert.Same(projectEngine, snapshot.GetProjectEngine());
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
|
|
@ -104,15 +310,13 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
Assert.False(snapshot.IsInitialized);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
Assert.Equal(ProjectChangeKind.ProjectAdded, ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void HostProjectAdded_FindsWorkspaceProject_NotifiesListeners_AndStartsBackgroundWorker()
|
||||
public void HostProjectAdded_FindsWorkspaceProject_NotifiesListeners()
|
||||
{
|
||||
// Arrange
|
||||
Assert.True(Workspace.TryApplyChanges(WorkspaceProject.Solution));
|
||||
|
|
@ -122,60 +326,85 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
Assert.True(snapshot.IsInitialized);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.True(ProjectManager.WorkerStarted);
|
||||
Assert.Equal(ProjectChangeKind.ProjectAdded, ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void HostProjectChanged_WithoutWorkspaceProject_NotifiesListeners_AndDoesNotStartBackgroundWorker()
|
||||
public void HostProjectChanged_ConfigurationChange_WithoutWorkspaceProject_NotifiesListeners()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var project = new HostProject(HostProject.FilePath, FallbackRazorConfiguration.MVC_1_0); // Simulate a project change
|
||||
|
||||
// Act
|
||||
ProjectManager.HostProjectChanged(project);
|
||||
ProjectManager.HostProjectChanged(HostProjectWithConfigurationChange);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProjectWithConfigurationChange);
|
||||
Assert.False(snapshot.IsInitialized);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
Assert.Equal(ProjectChangeKind.ProjectChanged, ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void HostProjectChanged_WithWorkspaceProject_RetainsComputedState_NotifiesListeners_AndStartsBackgroundWorker()
|
||||
public void HostProjectChanged_ConfigurationChange_WithWorkspaceProject_NotifiesListeners()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Adding some computed state
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
var updateContext = snapshot.CreateUpdateContext();
|
||||
ProjectManager.ProjectUpdated(updateContext);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var project = new HostProject(HostProject.FilePath, FallbackRazorConfiguration.MVC_1_0); // Simulate a project change
|
||||
|
||||
// Act
|
||||
ProjectManager.HostProjectChanged(project);
|
||||
ProjectManager.HostProjectChanged(HostProjectWithConfigurationChange);
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(project);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProjectWithConfigurationChange);
|
||||
Assert.True(snapshot.IsInitialized);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.True(ProjectManager.WorkerStarted);
|
||||
Assert.Equal(ProjectChangeKind.ProjectChanged, ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void HostProjectChanged_ConfigurationChange_DoesNotCacheProjectEngine()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
var projectEngine = snapshot.GetProjectEngine();
|
||||
|
||||
// Act
|
||||
ProjectManager.HostProjectChanged(HostProjectWithConfigurationChange);
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(HostProjectWithConfigurationChange);
|
||||
Assert.NotSame(projectEngine, snapshot.GetProjectEngine());
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task HostProjectChanged_ConfigurationChange_DoesNotCacheComputedState()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Adding some computed state
|
||||
await snapshot.GetTagHelpersAsync();
|
||||
|
||||
// Act
|
||||
ProjectManager.HostProjectChanged(HostProjectWithConfigurationChange);
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(HostProjectWithConfigurationChange);
|
||||
Assert.False(snapshot.TryGetTagHelpers(out var _));
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
|
|
@ -189,8 +418,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// Assert
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
|
|
@ -206,277 +434,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// Assert
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void ProjectUpdated_WithComputedState_IgnoresUnknownProject()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectUpdated(new ProjectSnapshotUpdateContext("Test", HostProject, WorkspaceProject, VersionStamp.Default));
|
||||
|
||||
// Assert
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void ProjectUpdated_WhenHostProjectChanged_MadeClean_NotifiesListeners_AndDoesNotStartBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var project = new HostProject(HostProject.FilePath, FallbackRazorConfiguration.MVC_1_0); // Simulate a project change
|
||||
ProjectManager.HostProjectChanged(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Generate the update
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
var updateContext = snapshot.CreateUpdateContext();
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectUpdated(updateContext);
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(project);
|
||||
Assert.False(snapshot.IsDirty);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void ProjectUpdated_WhenWorkspaceProjectChanged_MadeClean_NotifiesListeners_AndDoesNotStartBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var project = WorkspaceProject.WithAssemblyName("Test1"); // Simulate a project change
|
||||
ProjectManager.WorkspaceProjectChanged(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Generate the update
|
||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
var updateContext = snapshot.CreateUpdateContext();
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectUpdated(updateContext);
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(project);
|
||||
Assert.False(snapshot.IsDirty);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void ProjectUpdated_WhenHostProjectChanged_StillDirty_WithSignificantChanges_NotifiesListeners_AndStartsBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Generate the update
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
var updateContext = snapshot.CreateUpdateContext();
|
||||
|
||||
var project = new HostProject(HostProject.FilePath, FallbackRazorConfiguration.MVC_1_0); // Simulate a project change
|
||||
ProjectManager.HostProjectChanged(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectUpdated(updateContext);
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(project);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.True(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void WorkspaceProjectChanged_BackgroundUpdate_StillDirty_WithSignificantChanges_NotifiesListeners_AndStartsBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Generate the update
|
||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
var updateContext = snapshot.CreateUpdateContext();
|
||||
|
||||
var project = WorkspaceProject.WithAssemblyName("Test1"); // Simulate a project change
|
||||
ProjectManager.WorkspaceProjectChanged(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectUpdated(updateContext);
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(project);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.True(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[Fact(Skip = "We no longer have any background-computed state")]
|
||||
public void ProjectUpdated_WhenHostProjectChanged_StillDirty_WithoutSignificantChanges_DoesNotNotifyListeners_AndStartsBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Generate an update based on the original state
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
var updateContext = snapshot.CreateUpdateContext();
|
||||
ProjectManager.ProjectUpdated(updateContext);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var project = new HostProject(HostProject.FilePath, FallbackRazorConfiguration.MVC_1_0); // Simulate a project change
|
||||
ProjectManager.HostProjectChanged(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Now start computing another update
|
||||
snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
updateContext = snapshot.CreateUpdateContext();
|
||||
|
||||
project = new HostProject(HostProject.FilePath, FallbackRazorConfiguration.MVC_1_1); // Simulate a project change
|
||||
ProjectManager.HostProjectChanged(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectUpdated(updateContext); // Still dirty because the project changed while computing the update
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(project);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.True(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[Fact(Skip = "We no longer have any background-computed state")]
|
||||
public void ProjectUpdated_WhenWorkspaceProjectChanged_StillDirty_WithoutSignificantChanges_DoesNotNotifyListeners_AndStartsBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Generate an update based on the original state
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
var updateContext = snapshot.CreateUpdateContext();
|
||||
ProjectManager.ProjectUpdated(updateContext);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var project = WorkspaceProject.WithAssemblyName("Test1"); // Simulate a project change
|
||||
ProjectManager.WorkspaceProjectChanged(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Now start computing another update
|
||||
snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
updateContext = snapshot.CreateUpdateContext();
|
||||
|
||||
project = project.WithAssemblyName("Test2"); // Simulate a project change
|
||||
ProjectManager.WorkspaceProjectChanged(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectUpdated(updateContext); // Still dirty because the project changed while computing the update
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(project);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.True(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void ProjectUpdated_WhenHostProjectRemoved_DiscardsUpdate()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Generate the update
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
var updateContext = snapshot.CreateUpdateContext();
|
||||
|
||||
ProjectManager.HostProjectRemoved(HostProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectUpdated(updateContext);
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
Assert.Null(snapshot);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void ProjectUpdated_WhenWorkspaceProjectRemoved_DiscardsUpdate()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Generate the update
|
||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
var updateContext = snapshot.CreateUpdateContext();
|
||||
|
||||
ProjectManager.WorkspaceProjectRemoved(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectUpdated(updateContext);
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void ProjectUpdated_BackgroundUpdate_MadeClean_WithSignificantChanges_NotifiesListeners_AndDoesNotStartBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Generate the update
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
var updateContext = snapshot.CreateUpdateContext();
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectUpdated(updateContext);
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.False(snapshot.IsDirty);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
Assert.Equal(ProjectChangeKind.ProjectRemoved, ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
|
|
@ -490,8 +448,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// Assert
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
|
|
@ -508,8 +465,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.False(snapshot.IsInitialized);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
|
|
@ -527,8 +483,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.Same(WorkspaceProject, snapshot.WorkspaceProject);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
|
|
@ -545,12 +500,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.False(snapshot.IsInitialized);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void WorkspaceProjectAdded_WithHostProject_NotifiesListenters_AndStartsBackgroundWorker()
|
||||
public void WorkspaceProjectAdded_WithHostProject_NotifiesListenters()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
|
|
@ -561,11 +515,45 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
Assert.True(snapshot.IsInitialized);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.True(ProjectManager.WorkerStarted);
|
||||
Assert.Equal(ProjectChangeKind.ProjectChanged, ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void WorkspaceProjectChanged_WithHostProject_NotifiesListenters()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.WorkspaceProjectChanged(WorkspaceProject.WithAssemblyName("Test1"));
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.True(snapshot.IsInitialized);
|
||||
|
||||
Assert.Equal(ProjectChangeKind.ProjectChanged, ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void WorkspaceProjectChanged_WithHostProject_CanNoOp()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.WorkspaceProjectChanged(WorkspaceProject);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.True(snapshot.IsInitialized);
|
||||
|
||||
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
|
|
@ -583,8 +571,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// Assert
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
|
|
@ -604,11 +591,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.False(snapshot.IsInitialized);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
|
||||
[ForegroundFact]
|
||||
public void WorkspaceProjectChanged_IgnoresProjectWithoutFilePath()
|
||||
{
|
||||
|
|
@ -626,8 +611,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.False(snapshot.IsInitialized);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
|
|
@ -645,65 +629,46 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.Same(WorkspaceProject, snapshot.WorkspaceProject);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void WorkspaceProjectChanged_MadeDirty_RetainsComputedState_NotifiesListeners_AndStartsBackgroundWorker()
|
||||
public async Task WorkspaceProjectRemoved_DoesNotRemoveProject_RemovesTagHelpers()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Generate the update
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
var updateContext = snapshot.CreateUpdateContext();
|
||||
ProjectManager.ProjectUpdated(updateContext);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var project = WorkspaceProject.WithAssemblyName("Test1"); // Simulate a project change
|
||||
|
||||
// Act
|
||||
ProjectManager.WorkspaceProjectChanged(project);
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(project);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.True(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void WorkspaceProjectRemoved_WithHostProject_DoesNotRemoveProject()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
// Adding some computed state
|
||||
await snapshot.GetTagHelpersAsync();
|
||||
|
||||
// Act
|
||||
ProjectManager.WorkspaceProjectRemoved(WorkspaceProject);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.False(snapshot.IsInitialized);
|
||||
Assert.False(snapshot.TryGetTagHelpers(out var _));
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
Assert.Equal(ProjectChangeKind.ProjectChanged, ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void WorkspaceProjectRemoved_WithHostProject_FallsBackToSecondProject()
|
||||
public async Task WorkspaceProjectRemoved_FallsBackToSecondProject()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
|
||||
// Adding some computed state
|
||||
await snapshot.GetTagHelpersAsync();
|
||||
|
||||
// Sets up a solution where the which has WorkspaceProjectWithDifferentTfm but not WorkspaceProject
|
||||
// This will enable us to fall back and find the WorkspaceProjectWithDifferentTfm
|
||||
Assert.True(Workspace.TryApplyChanges(WorkspaceProjectWithDifferentTfm.Solution));
|
||||
|
|
@ -712,13 +677,12 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
ProjectManager.WorkspaceProjectRemoved(WorkspaceProject);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.True(snapshot.IsInitialized);
|
||||
Assert.Equal(WorkspaceProjectWithDifferentTfm.Id, snapshot.WorkspaceProject.Id);
|
||||
Assert.False(snapshot.TryGetTagHelpers(out var _));
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.True(ProjectManager.WorkerStarted);
|
||||
Assert.Equal(ProjectChangeKind.ProjectChanged, ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
|
|
@ -736,8 +700,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.Same(WorkspaceProject, snapshot.WorkspaceProject);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
|
|
@ -755,8 +718,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.False(snapshot.IsInitialized);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
|
|
@ -774,8 +736,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.False(snapshot.IsInitialized);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
|
|
@ -789,20 +750,17 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// Assert
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
private class TestProjectSnapshotManager : DefaultProjectSnapshotManager
|
||||
{
|
||||
public TestProjectSnapshotManager(ForegroundDispatcher dispatcher, IEnumerable<ProjectSnapshotChangeTrigger> triggers, Workspace workspace)
|
||||
: base(dispatcher, Mock.Of<ErrorReporter>(), Mock.Of<ProjectSnapshotWorker>(), triggers, workspace)
|
||||
: base(dispatcher, Mock.Of<ErrorReporter>(), triggers, workspace)
|
||||
{
|
||||
}
|
||||
|
||||
public bool ListenersNotified { get; private set; }
|
||||
|
||||
public bool WorkerStarted { get; private set; }
|
||||
public ProjectChangeKind? ListenersNotifiedOf { get; private set; }
|
||||
|
||||
public DefaultProjectSnapshot GetSnapshot(HostProject hostProject)
|
||||
{
|
||||
|
|
@ -816,21 +774,12 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
public void Reset()
|
||||
{
|
||||
ListenersNotified = false;
|
||||
WorkerStarted = false;
|
||||
ListenersNotifiedOf = null;
|
||||
}
|
||||
|
||||
protected override void NotifyListeners(ProjectChangeEventArgs e)
|
||||
{
|
||||
ListenersNotified = true;
|
||||
}
|
||||
|
||||
protected override void NotifyBackgroundWorker(ProjectSnapshotUpdateContext context)
|
||||
{
|
||||
Assert.NotNull(context.HostProject);
|
||||
Assert.NotNull(context.WorkspaceProject);
|
||||
|
||||
WorkerStarted = true;
|
||||
ListenersNotifiedOf = e.Kind;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,12 +7,11 @@ using System.Collections.Immutable;
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.VisualStudio.LanguageServices.Razor;
|
||||
using Microsoft.VisualStudio.ProjectSystem;
|
||||
using Microsoft.VisualStudio.ProjectSystem.Properties;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
using ProjectStateItem = System.Collections.Generic.KeyValuePair<string, System.Collections.Immutable.IImmutableDictionary<string, string>>;
|
||||
using ItemCollection = Microsoft.VisualStudio.ProjectSystem.ItemCollection;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
|
|
@ -22,8 +21,21 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
Workspace = new AdhocWorkspace();
|
||||
ProjectManager = new TestProjectSnapshotManager(Dispatcher, Workspace);
|
||||
|
||||
ConfigurationItems = new ItemCollection(Rules.RazorConfiguration.SchemaName);
|
||||
ExtensionItems = new ItemCollection(Rules.RazorExtension.SchemaName);
|
||||
DocumentItems = new ItemCollection(Rules.RazorGenerateWithTargetPath.SchemaName);
|
||||
RazorGeneralProperties = new PropertyCollection(Rules.RazorGeneral.SchemaName);
|
||||
}
|
||||
|
||||
private ItemCollection ConfigurationItems { get; }
|
||||
|
||||
private ItemCollection ExtensionItems { get; }
|
||||
|
||||
private ItemCollection DocumentItems { get; }
|
||||
|
||||
private PropertyCollection RazorGeneralProperties { get; }
|
||||
|
||||
private TestProjectSnapshotManager ProjectManager { get; }
|
||||
|
||||
private Workspace Workspace { get; }
|
||||
|
|
@ -285,11 +297,13 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public void TryGetConfiguredExtensionNames_FailsIfNoExtensions()
|
||||
{
|
||||
// Arrange
|
||||
var extensions = new Dictionary<string, string>().ToImmutableDictionary();
|
||||
var configurationItem = new ProjectStateItem(Rules.RazorConfiguration.SchemaName, extensions);
|
||||
var items = new ItemCollection(Rules.RazorConfiguration.SchemaName);
|
||||
items.Item("Test");
|
||||
|
||||
var item = items.ToSnapshot().Items.Single();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfiguredExtensionNames(configurationItem, out var configuredExtensionnames);
|
||||
var result = DefaultRazorProjectHost.TryGetExtensionNames(item, out var configuredExtensionnames);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
|
|
@ -300,14 +314,14 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public void TryGetConfiguredExtensionNames_FailsIfEmptyExtensions()
|
||||
{
|
||||
// Arrange
|
||||
var extensions = new Dictionary<string, string>()
|
||||
{
|
||||
[Rules.RazorConfiguration.ExtensionsProperty] = string.Empty
|
||||
}.ToImmutableDictionary();
|
||||
var configurationItem = new ProjectStateItem(Rules.RazorConfiguration.SchemaName, extensions);
|
||||
var items = new ItemCollection(Rules.RazorConfiguration.SchemaName);
|
||||
items.Item("Test");
|
||||
items.Property("Test", Rules.RazorConfiguration.ExtensionsProperty, string.Empty);
|
||||
|
||||
var item = items.ToSnapshot().Items.Single();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfiguredExtensionNames(configurationItem, out var configuredExtensionNames);
|
||||
var result = DefaultRazorProjectHost.TryGetExtensionNames(item, out var configuredExtensionNames);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
|
|
@ -319,14 +333,15 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
// Arrange
|
||||
var expectedExtensionName = "SomeExtensionName";
|
||||
var extensions = new Dictionary<string, string>()
|
||||
{
|
||||
[Rules.RazorConfiguration.ExtensionsProperty] = expectedExtensionName
|
||||
}.ToImmutableDictionary();
|
||||
var configurationItem = new ProjectStateItem(Rules.RazorConfiguration.SchemaName, extensions);
|
||||
|
||||
var items = new ItemCollection(Rules.RazorConfiguration.SchemaName);
|
||||
items.Item("Test");
|
||||
items.Property("Test", Rules.RazorConfiguration.ExtensionsProperty, "SomeExtensionName");
|
||||
|
||||
var item = items.ToSnapshot().Items.Single();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfiguredExtensionNames(configurationItem, out var configuredExtensionNames);
|
||||
var result = DefaultRazorProjectHost.TryGetExtensionNames(item, out var configuredExtensionNames);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
|
|
@ -338,14 +353,14 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public void TryGetConfiguredExtensionNames_SucceedsIfMultipleExtensions()
|
||||
{
|
||||
// Arrange
|
||||
var extensions = new Dictionary<string, string>()
|
||||
{
|
||||
[Rules.RazorConfiguration.ExtensionsProperty] = "SomeExtensionName;SomeOtherExtensionName"
|
||||
}.ToImmutableDictionary();
|
||||
var configurationItem = new ProjectStateItem(Rules.RazorConfiguration.SchemaName, extensions);
|
||||
var items = new ItemCollection(Rules.RazorConfiguration.SchemaName);
|
||||
items.Item("Test");
|
||||
items.Property("Test", Rules.RazorConfiguration.ExtensionsProperty, "SomeExtensionName;SomeOtherExtensionName");
|
||||
|
||||
var item = items.ToSnapshot().Items.Single();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfiguredExtensionNames(configurationItem, out var configuredExtensionNames);
|
||||
var result = DefaultRazorProjectHost.TryGetExtensionNames(item, out var configuredExtensionNames);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
|
|
@ -597,7 +612,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public async Task DefaultRazorProjectHost_ForegroundThread_CreateAndDispose_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
var services = new TestProjectSystemServices("c:\\MyProject\\Test.csproj");
|
||||
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
||||
|
||||
// Act & Assert
|
||||
|
|
@ -612,7 +627,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public async Task DefaultRazorProjectHost_BackgroundThread_CreateAndDispose_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
var services = new TestProjectSystemServices("c:\\MyProject\\Test.csproj");
|
||||
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
||||
|
||||
// Act & Assert
|
||||
|
|
@ -623,41 +638,50 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
Assert.Empty(ProjectManager.Projects);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task OnProjectChanged_ReadsProperties_InitializesProject()
|
||||
[ForegroundFact] // This can happen if the .xaml files aren't included correctly.
|
||||
public async Task DefaultRazorProjectHost_OnProjectChanged_NoRulesDefined()
|
||||
{
|
||||
// Arrange
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorGeneral.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateProperties(Rules.RazorGeneral.SchemaName, new Dictionary<string, string>()
|
||||
{
|
||||
{ Rules.RazorGeneral.RazorLangVersionProperty, "2.1" },
|
||||
{ Rules.RazorGeneral.RazorDefaultConfigurationProperty, "MVC-2.1" },
|
||||
}),
|
||||
},
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorConfiguration.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(Rules.RazorConfiguration.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "MVC-2.1", new Dictionary<string, string>() { { "Extensions", "MVC-2.1;Another-Thing" }, } },
|
||||
})
|
||||
},
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorExtension.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(Rules.RazorExtension.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "MVC-2.1", new Dictionary<string, string>(){ } },
|
||||
{ "Another-Thing", new Dictionary<string, string>(){ } },
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
||||
|
||||
// Act & Assert
|
||||
await Task.Run(async () => await host.LoadAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
await Task.Run(async () => await host.OnProjectChanged(services.CreateUpdate(changes)));
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task OnProjectChanged_ReadsProperties_InitializesProject()
|
||||
{
|
||||
// Arrange
|
||||
RazorGeneralProperties.Property(Rules.RazorGeneral.RazorLangVersionProperty, "2.1");
|
||||
RazorGeneralProperties.Property(Rules.RazorGeneral.RazorDefaultConfigurationProperty, "MVC-2.1");
|
||||
|
||||
ConfigurationItems.Item("MVC-2.1");
|
||||
ConfigurationItems.Property("MVC-2.1", Rules.RazorConfiguration.ExtensionsProperty, "MVC-2.1;Another-Thing");
|
||||
|
||||
ExtensionItems.Item("MVC-2.1");
|
||||
ExtensionItems.Item("Another-Thing");
|
||||
|
||||
DocumentItems.Item("File.cshtml");
|
||||
DocumentItems.Property("File.cshtml", Rules.RazorGenerateWithTargetPath.TargetPathProperty, "File.cshtml");
|
||||
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
RazorGeneralProperties.ToChange(),
|
||||
ConfigurationItems.ToChange(),
|
||||
ExtensionItems.ToChange(),
|
||||
DocumentItems.ToChange(),
|
||||
};
|
||||
|
||||
var services = new TestProjectSystemServices("c:\\MyProject\\Test.csproj");
|
||||
|
||||
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
||||
|
||||
|
|
@ -669,7 +693,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
// Assert
|
||||
var snapshot = Assert.Single(ProjectManager.Projects);
|
||||
Assert.Equal("Test.csproj", snapshot.FilePath);
|
||||
Assert.Equal("c:\\MyProject\\Test.csproj", snapshot.FilePath);
|
||||
|
||||
Assert.Equal(RazorLanguageVersion.Version_2_1, snapshot.Configuration.LanguageVersion);
|
||||
Assert.Equal("MVC-2.1", snapshot.Configuration.ConfigurationName);
|
||||
|
|
@ -678,6 +702,15 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
e => Assert.Equal("MVC-2.1", e.ExtensionName),
|
||||
e => Assert.Equal("Another-Thing", e.ExtensionName));
|
||||
|
||||
Assert.Collection(
|
||||
snapshot.DocumentFilePaths.OrderBy(d => d),
|
||||
d =>
|
||||
{
|
||||
var document = snapshot.GetDocument(d);
|
||||
Assert.Equal("c:\\MyProject\\File.cshtml", document.FilePath);
|
||||
Assert.Equal("File.cshtml", document.TargetPath);
|
||||
});
|
||||
|
||||
await Task.Run(async () => await host.DisposeAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
}
|
||||
|
|
@ -686,34 +719,24 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public async Task OnProjectChanged_NoVersionFound_DoesNotIniatializeProject()
|
||||
{
|
||||
// Arrange
|
||||
RazorGeneralProperties.Property(Rules.RazorGeneral.RazorLangVersionProperty, "");
|
||||
RazorGeneralProperties.Property(Rules.RazorGeneral.RazorDefaultConfigurationProperty, "");
|
||||
|
||||
ConfigurationItems.Item("TestConfiguration");
|
||||
|
||||
ExtensionItems.Item("TestExtension");
|
||||
|
||||
DocumentItems.Item("File.cshtml");
|
||||
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorGeneral.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateProperties(Rules.RazorGeneral.SchemaName, new Dictionary<string, string>()
|
||||
{
|
||||
{ Rules.RazorGeneral.RazorLangVersionProperty, "" },
|
||||
{ Rules.RazorGeneral.RazorDefaultConfigurationProperty, "" },
|
||||
}),
|
||||
},
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorConfiguration.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(Rules.RazorConfiguration.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
})
|
||||
},
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorExtension.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(Rules.RazorExtension.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
})
|
||||
}
|
||||
RazorGeneralProperties.ToChange(),
|
||||
ConfigurationItems.ToChange(),
|
||||
ExtensionItems.ToChange(),
|
||||
DocumentItems.ToChange(),
|
||||
};
|
||||
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
var services = new TestProjectSystemServices("c:\\MyProject\\Test.csproj");
|
||||
|
||||
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
||||
|
||||
|
|
@ -734,37 +757,27 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public async Task OnProjectChanged_UpdateProject_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
RazorGeneralProperties.Property(Rules.RazorGeneral.RazorLangVersionProperty, "2.1");
|
||||
RazorGeneralProperties.Property(Rules.RazorGeneral.RazorDefaultConfigurationProperty, "MVC-2.1");
|
||||
|
||||
ConfigurationItems.Item("MVC-2.1");
|
||||
ConfigurationItems.Property("MVC-2.1", Rules.RazorConfiguration.ExtensionsProperty, "MVC-2.1;Another-Thing");
|
||||
|
||||
ExtensionItems.Item("MVC-2.1");
|
||||
ExtensionItems.Item("Another-Thing");
|
||||
|
||||
DocumentItems.Item("File.cshtml");
|
||||
DocumentItems.Property("File.cshtml", Rules.RazorGenerateWithTargetPath.TargetPathProperty, "File.cshtml");
|
||||
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorGeneral.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateProperties(Rules.RazorGeneral.SchemaName, new Dictionary<string, string>()
|
||||
{
|
||||
{ Rules.RazorGeneral.RazorLangVersionProperty, "2.1" },
|
||||
{ Rules.RazorGeneral.RazorDefaultConfigurationProperty, "MVC-2.1" },
|
||||
}),
|
||||
},
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorConfiguration.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(Rules.RazorConfiguration.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "MVC-2.1", new Dictionary<string, string>() { { "Extensions", "MVC-2.1;Another-Thing" }, } },
|
||||
})
|
||||
},
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorExtension.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(Rules.RazorExtension.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "MVC-2.1", new Dictionary<string, string>(){ } },
|
||||
{ "Another-Thing", new Dictionary<string, string>(){ } },
|
||||
})
|
||||
}
|
||||
RazorGeneralProperties.ToChange(),
|
||||
ConfigurationItems.ToChange(),
|
||||
ExtensionItems.ToChange(),
|
||||
DocumentItems.ToChange(),
|
||||
};
|
||||
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
var services = new TestProjectSystemServices("c:\\MyProject\\Test.csproj");
|
||||
|
||||
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
||||
|
||||
|
|
@ -776,7 +789,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
// Assert - 1
|
||||
var snapshot = Assert.Single(ProjectManager.Projects);
|
||||
Assert.Equal("Test.csproj", snapshot.FilePath);
|
||||
Assert.Equal("c:\\MyProject\\Test.csproj", snapshot.FilePath);
|
||||
|
||||
Assert.Equal(RazorLanguageVersion.Version_2_1, snapshot.Configuration.LanguageVersion);
|
||||
Assert.Equal("MVC-2.1", snapshot.Configuration.ConfigurationName);
|
||||
|
|
@ -785,17 +798,39 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
e => Assert.Equal("MVC-2.1", e.ExtensionName),
|
||||
e => Assert.Equal("Another-Thing", e.ExtensionName));
|
||||
|
||||
Assert.Collection(
|
||||
snapshot.DocumentFilePaths.OrderBy(d => d),
|
||||
d =>
|
||||
{
|
||||
var document = snapshot.GetDocument(d);
|
||||
Assert.Equal("c:\\MyProject\\File.cshtml", document.FilePath);
|
||||
Assert.Equal("File.cshtml", document.TargetPath);
|
||||
});
|
||||
|
||||
// Act - 2
|
||||
changes[0].After.SetProperty(Rules.RazorGeneral.RazorLangVersionProperty, "2.0");
|
||||
changes[0].After.SetProperty(Rules.RazorGeneral.RazorDefaultConfigurationProperty, "MVC-2.0");
|
||||
changes[1].After.SetItem("MVC-2.0", new Dictionary<string, string>() { { "Extensions", "MVC-2.0;Another-Thing" }, });
|
||||
changes[2].After.SetItem("MVC-2.0", new Dictionary<string, string>());
|
||||
RazorGeneralProperties.Property(Rules.RazorGeneral.RazorLangVersionProperty, "2.0");
|
||||
RazorGeneralProperties.Property(Rules.RazorGeneral.RazorDefaultConfigurationProperty, "MVC-2.0");
|
||||
ConfigurationItems.RemoveItem("MVC-2.1");
|
||||
ConfigurationItems.Item("MVC-2.0", new Dictionary<string, string>() { { "Extensions", "MVC-2.0;Another-Thing" }, });
|
||||
ExtensionItems.Item("MVC-2.0");
|
||||
DocumentItems.Item("c:\\AnotherProject\\AnotherFile.cshtml", new Dictionary<string, string>()
|
||||
{
|
||||
{ Rules.RazorGenerateWithTargetPath.TargetPathProperty, "Pages\\AnotherFile.cshtml" },
|
||||
});
|
||||
|
||||
changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
RazorGeneralProperties.ToChange(changes[0].After),
|
||||
ConfigurationItems.ToChange(changes[1].After),
|
||||
ExtensionItems.ToChange(changes[2].After),
|
||||
DocumentItems.ToChange(changes[3].After),
|
||||
};
|
||||
|
||||
await Task.Run(async () => await host.OnProjectChanged(services.CreateUpdate(changes)));
|
||||
|
||||
// Assert - 2
|
||||
snapshot = Assert.Single(ProjectManager.Projects);
|
||||
Assert.Equal("Test.csproj", snapshot.FilePath);
|
||||
Assert.Equal("c:\\MyProject\\Test.csproj", snapshot.FilePath);
|
||||
|
||||
Assert.Equal(RazorLanguageVersion.Version_2_0, snapshot.Configuration.LanguageVersion);
|
||||
Assert.Equal("MVC-2.0", snapshot.Configuration.ConfigurationName);
|
||||
|
|
@ -804,6 +839,21 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
e => Assert.Equal("Another-Thing", e.ExtensionName),
|
||||
e => Assert.Equal("MVC-2.0", e.ExtensionName));
|
||||
|
||||
Assert.Collection(
|
||||
snapshot.DocumentFilePaths.OrderBy(d => d),
|
||||
d =>
|
||||
{
|
||||
var document = snapshot.GetDocument(d);
|
||||
Assert.Equal("c:\\AnotherProject\\AnotherFile.cshtml", document.FilePath);
|
||||
Assert.Equal("Pages\\AnotherFile.cshtml", document.TargetPath);
|
||||
},
|
||||
d =>
|
||||
{
|
||||
var document = snapshot.GetDocument(d);
|
||||
Assert.Equal("c:\\MyProject\\File.cshtml", document.FilePath);
|
||||
Assert.Equal("File.cshtml", document.TargetPath);
|
||||
});
|
||||
|
||||
await Task.Run(async () => await host.DisposeAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
}
|
||||
|
|
@ -812,37 +862,27 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public async Task OnProjectChanged_VersionRemoved_DeinitializesProject()
|
||||
{
|
||||
// Arrange
|
||||
RazorGeneralProperties.Property(Rules.RazorGeneral.RazorLangVersionProperty, "2.1");
|
||||
RazorGeneralProperties.Property(Rules.RazorGeneral.RazorDefaultConfigurationProperty, "MVC-2.1");
|
||||
|
||||
ConfigurationItems.Item("MVC-2.1");
|
||||
ConfigurationItems.Property("MVC-2.1", Rules.RazorConfiguration.ExtensionsProperty, "MVC-2.1;Another-Thing");
|
||||
|
||||
ExtensionItems.Item("MVC-2.1");
|
||||
ExtensionItems.Item("Another-Thing");
|
||||
|
||||
DocumentItems.Item("File.cshtml");
|
||||
DocumentItems.Property("File.cshtml", Rules.RazorGenerateWithTargetPath.TargetPathProperty, "File.cshtml");
|
||||
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorGeneral.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateProperties(Rules.RazorGeneral.SchemaName, new Dictionary<string, string>()
|
||||
{
|
||||
{ Rules.RazorGeneral.RazorLangVersionProperty, "2.1" },
|
||||
{ Rules.RazorGeneral.RazorDefaultConfigurationProperty, "MVC-2.1" },
|
||||
}),
|
||||
},
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorConfiguration.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(Rules.RazorConfiguration.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "MVC-2.1", new Dictionary<string, string>() { { "Extensions", "MVC-2.1;Another-Thing" }, } },
|
||||
})
|
||||
},
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorExtension.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(Rules.RazorExtension.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "MVC-2.1", new Dictionary<string, string>(){ } },
|
||||
{ "Another-Thing", new Dictionary<string, string>(){ } },
|
||||
})
|
||||
}
|
||||
RazorGeneralProperties.ToChange(),
|
||||
ConfigurationItems.ToChange(),
|
||||
ExtensionItems.ToChange(),
|
||||
DocumentItems.ToChange(),
|
||||
};
|
||||
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
var services = new TestProjectSystemServices("c:\\MyProject\\Test.csproj");
|
||||
|
||||
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
||||
|
||||
|
|
@ -854,7 +894,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
// Assert - 1
|
||||
var snapshot = Assert.Single(ProjectManager.Projects);
|
||||
Assert.Equal("Test.csproj", snapshot.FilePath);
|
||||
Assert.Equal("c:\\MyProject\\Test.csproj", snapshot.FilePath);
|
||||
|
||||
Assert.Equal(RazorLanguageVersion.Version_2_1, snapshot.Configuration.LanguageVersion);
|
||||
Assert.Equal("MVC-2.1", snapshot.Configuration.ConfigurationName);
|
||||
|
|
@ -864,8 +904,16 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
e => Assert.Equal("Another-Thing", e.ExtensionName));
|
||||
|
||||
// Act - 2
|
||||
changes[0].After.SetProperty(Rules.RazorGeneral.RazorLangVersionProperty, "");
|
||||
changes[0].After.SetProperty(Rules.RazorGeneral.RazorDefaultConfigurationProperty, "");
|
||||
RazorGeneralProperties.Property(Rules.RazorGeneral.RazorLangVersionProperty, "");
|
||||
RazorGeneralProperties.Property(Rules.RazorGeneral.RazorDefaultConfigurationProperty, "");
|
||||
|
||||
changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
RazorGeneralProperties.ToChange(changes[0].After),
|
||||
ConfigurationItems.ToChange(changes[1].After),
|
||||
ExtensionItems.ToChange(changes[2].After),
|
||||
DocumentItems.ToChange(changes[3].After),
|
||||
};
|
||||
|
||||
await Task.Run(async () => await host.OnProjectChanged(services.CreateUpdate(changes)));
|
||||
|
||||
|
|
@ -880,37 +928,27 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public async Task OnProjectChanged_AfterDispose_IgnoresUpdate()
|
||||
{
|
||||
// Arrange
|
||||
RazorGeneralProperties.Property(Rules.RazorGeneral.RazorLangVersionProperty, "2.1");
|
||||
RazorGeneralProperties.Property(Rules.RazorGeneral.RazorDefaultConfigurationProperty, "MVC-2.1");
|
||||
|
||||
ConfigurationItems.Item("MVC-2.1");
|
||||
ConfigurationItems.Property("MVC-2.1", Rules.RazorConfiguration.ExtensionsProperty, "MVC-2.1;Another-Thing");
|
||||
|
||||
ExtensionItems.Item("MVC-2.1");
|
||||
ExtensionItems.Item("Another-Thing");
|
||||
|
||||
DocumentItems.Item("File.cshtml");
|
||||
DocumentItems.Property("File.cshtml", Rules.RazorGenerateWithTargetPath.TargetPathProperty, "File.cshtml");
|
||||
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorGeneral.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateProperties(Rules.RazorGeneral.SchemaName, new Dictionary<string, string>()
|
||||
{
|
||||
{ Rules.RazorGeneral.RazorLangVersionProperty, "2.1" },
|
||||
{ Rules.RazorGeneral.RazorDefaultConfigurationProperty, "MVC-2.1" },
|
||||
}),
|
||||
},
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorConfiguration.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(Rules.RazorConfiguration.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "MVC-2.1", new Dictionary<string, string>() { { "Extensions", "MVC-2.1;Another-Thing" }, } },
|
||||
})
|
||||
},
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorExtension.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(Rules.RazorExtension.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "MVC-2.1", new Dictionary<string, string>(){ } },
|
||||
{ "Another-Thing", new Dictionary<string, string>(){ } },
|
||||
})
|
||||
}
|
||||
RazorGeneralProperties.ToChange(),
|
||||
ConfigurationItems.ToChange(),
|
||||
ExtensionItems.ToChange(),
|
||||
DocumentItems.ToChange(),
|
||||
};
|
||||
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
var services = new TestProjectSystemServices("c:\\MyProject\\Test.csproj");
|
||||
|
||||
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
||||
|
||||
|
|
@ -922,7 +960,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
// Assert - 1
|
||||
var snapshot = Assert.Single(ProjectManager.Projects);
|
||||
Assert.Equal("Test.csproj", snapshot.FilePath);
|
||||
Assert.Equal("c:\\MyProject\\Test.csproj", snapshot.FilePath);
|
||||
|
||||
Assert.Equal(RazorLanguageVersion.Version_2_1, snapshot.Configuration.LanguageVersion);
|
||||
Assert.Equal("MVC-2.1", snapshot.Configuration.ConfigurationName);
|
||||
|
|
@ -938,9 +976,17 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
// Act - 3
|
||||
changes[0].After.SetProperty(Rules.RazorGeneral.RazorLangVersionProperty, "2.0");
|
||||
changes[0].After.SetProperty(Rules.RazorGeneral.RazorDefaultConfigurationProperty, "MVC-2.0");
|
||||
changes[1].After.SetItem("MVC-2.0", new Dictionary<string, string>() { { "Extensions", "MVC-2.0;Another-Thing" }, });
|
||||
RazorGeneralProperties.Property(Rules.RazorGeneral.RazorLangVersionProperty, "2.0");
|
||||
RazorGeneralProperties.Property(Rules.RazorGeneral.RazorDefaultConfigurationProperty, "MVC-2.0");
|
||||
ConfigurationItems.Item("MVC-2.0", new Dictionary<string, string>() { { "Extensions", "MVC-2.0;Another-Thing" }, });
|
||||
|
||||
changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
RazorGeneralProperties.ToChange(changes[0].After),
|
||||
ConfigurationItems.ToChange(changes[1].After),
|
||||
ExtensionItems.ToChange(changes[2].After),
|
||||
DocumentItems.ToChange(changes[3].After),
|
||||
};
|
||||
|
||||
await Task.Run(async () => await host.OnProjectChanged(services.CreateUpdate(changes)));
|
||||
|
||||
|
|
@ -952,37 +998,27 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public async Task OnProjectRenamed_RemovesHostProject_CopiesConfiguration()
|
||||
{
|
||||
// Arrange
|
||||
RazorGeneralProperties.Property(Rules.RazorGeneral.RazorLangVersionProperty, "2.1");
|
||||
RazorGeneralProperties.Property(Rules.RazorGeneral.RazorDefaultConfigurationProperty, "MVC-2.1");
|
||||
|
||||
ConfigurationItems.Item("MVC-2.1");
|
||||
ConfigurationItems.Property("MVC-2.1", Rules.RazorConfiguration.ExtensionsProperty, "MVC-2.1;Another-Thing");
|
||||
|
||||
ExtensionItems.Item("MVC-2.1");
|
||||
ExtensionItems.Item("Another-Thing");
|
||||
|
||||
DocumentItems.Item("File.cshtml");
|
||||
DocumentItems.Property("File.cshtml", Rules.RazorGenerateWithTargetPath.TargetPathProperty, "File.cshtml");
|
||||
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorGeneral.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateProperties(Rules.RazorGeneral.SchemaName, new Dictionary<string, string>()
|
||||
{
|
||||
{ Rules.RazorGeneral.RazorLangVersionProperty, "2.1" },
|
||||
{ Rules.RazorGeneral.RazorDefaultConfigurationProperty, "MVC-2.1" },
|
||||
}),
|
||||
},
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorConfiguration.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(Rules.RazorConfiguration.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "MVC-2.1", new Dictionary<string, string>() { { "Extensions", "MVC-2.1;Another-Thing" }, } },
|
||||
})
|
||||
},
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorExtension.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(Rules.RazorExtension.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "MVC-2.1", new Dictionary<string, string>(){ } },
|
||||
{ "Another-Thing", new Dictionary<string, string>(){ } },
|
||||
})
|
||||
}
|
||||
RazorGeneralProperties.ToChange(),
|
||||
ConfigurationItems.ToChange(),
|
||||
ExtensionItems.ToChange(),
|
||||
DocumentItems.ToChange(),
|
||||
};
|
||||
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
var services = new TestProjectSystemServices("c:\\MyProject\\Test.csproj");
|
||||
|
||||
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
||||
|
||||
|
|
@ -994,16 +1030,16 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
// Assert - 1
|
||||
var snapshot = Assert.Single(ProjectManager.Projects);
|
||||
Assert.Equal("Test.csproj", snapshot.FilePath);
|
||||
Assert.Equal("c:\\MyProject\\Test.csproj", snapshot.FilePath);
|
||||
Assert.Same("MVC-2.1", snapshot.Configuration.ConfigurationName);
|
||||
|
||||
// Act - 2
|
||||
services.UnconfiguredProject.FullPath = "Test2.csproj";
|
||||
services.UnconfiguredProject.FullPath = "c:\\AnotherProject\\Test2.csproj";
|
||||
await Task.Run(async () => await host.OnProjectRenamingAsync());
|
||||
|
||||
// Assert - 1
|
||||
snapshot = Assert.Single(ProjectManager.Projects);
|
||||
Assert.Equal("Test2.csproj", snapshot.FilePath);
|
||||
Assert.Equal("c:\\AnotherProject\\Test2.csproj", snapshot.FilePath);
|
||||
Assert.Same("MVC-2.1", snapshot.Configuration.ConfigurationName);
|
||||
|
||||
await Task.Run(async () => await host.DisposeAsync());
|
||||
|
|
@ -1012,12 +1048,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
private class TestProjectSnapshotManager : DefaultProjectSnapshotManager
|
||||
{
|
||||
public TestProjectSnapshotManager(ForegroundDispatcher dispatcher, Workspace workspace)
|
||||
: base(dispatcher, Mock.Of<ErrorReporter>(), Mock.Of<ProjectSnapshotWorker>(), Array.Empty<ProjectSnapshotChangeTrigger>(), workspace)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void NotifyBackgroundWorker(ProjectSnapshotUpdateContext context)
|
||||
public TestProjectSnapshotManager(ForegroundDispatcher dispatcher, Workspace workspace)
|
||||
: base(dispatcher, Mock.Of<ErrorReporter>(), Array.Empty<ProjectSnapshotChangeTrigger>(), workspace)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.LanguageServices.Razor;
|
||||
using Microsoft.VisualStudio.ProjectSystem;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
|
@ -17,8 +16,12 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
Workspace = new AdhocWorkspace();
|
||||
ProjectManager = new TestProjectSnapshotManager(Dispatcher, Workspace);
|
||||
|
||||
ReferenceItems = new ItemCollection(ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName);
|
||||
}
|
||||
|
||||
private ItemCollection ReferenceItems { get; }
|
||||
|
||||
private TestProjectSnapshotManager ProjectManager { get; }
|
||||
|
||||
private Workspace Workspace { get; }
|
||||
|
|
@ -53,20 +56,37 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
Assert.Empty(ProjectManager.Projects);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task OnProjectChanged_ReadsProperties_InitializesProject()
|
||||
[ForegroundFact] // This can happen if the .xaml files aren't included correctly.
|
||||
public async Task OnProjectChanged_NoRulesDefined()
|
||||
{
|
||||
// Arrange
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "c:\\nuget\\Microsoft.AspNetCore.Mvc.razor.dll", new Dictionary<string, string>() },
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
var host = new TestFallbackRazorProjectHost(services, Workspace, ProjectManager)
|
||||
{
|
||||
AssemblyVersion = new Version(2, 0),
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
await Task.Run(async () => await host.LoadAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
await Task.Run(async () => await host.OnProjectChanged(services.CreateUpdate(changes)));
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task OnProjectChanged_ReadsProperties_InitializesProject()
|
||||
{
|
||||
// Arrange
|
||||
ReferenceItems.Item("c:\\nuget\\Microsoft.AspNetCore.Mvc.razor.dll");
|
||||
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
ReferenceItems.ToChange(),
|
||||
};
|
||||
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
|
|
@ -97,14 +117,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// Arrange
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
}),
|
||||
},
|
||||
|
||||
ReferenceItems.ToChange(),
|
||||
};
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
|
||||
|
|
@ -127,16 +140,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public async Task OnProjectChanged_AssemblyFoundButCannotReadVersion_DoesNotIniatializeProject()
|
||||
{
|
||||
// Arrange
|
||||
ReferenceItems.Item("c:\\nuget\\Microsoft.AspNetCore.Mvc.razor.dll");
|
||||
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "c:\\nuget\\Microsoft.AspNetCore.Mvc.razor.dll", new Dictionary<string, string>() },
|
||||
}),
|
||||
},
|
||||
ReferenceItems.ToChange(),
|
||||
};
|
||||
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
|
|
@ -160,16 +168,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public async Task OnProjectChanged_UpdateProject_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
ReferenceItems.Item("c:\\nuget\\Microsoft.AspNetCore.Mvc.razor.dll");
|
||||
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "c:\\nuget\\Microsoft.AspNetCore.Mvc.razor.dll", new Dictionary<string, string>() },
|
||||
}),
|
||||
},
|
||||
ReferenceItems.ToChange(),
|
||||
};
|
||||
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
|
|
@ -207,16 +210,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public async Task OnProjectChanged_VersionRemoved_DeinitializesProject()
|
||||
{
|
||||
// Arrange
|
||||
ReferenceItems.Item("c:\\nuget\\Microsoft.AspNetCore.Mvc.razor.dll");
|
||||
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "c:\\nuget\\Microsoft.AspNetCore.Mvc.razor.dll", new Dictionary<string, string>() },
|
||||
}),
|
||||
},
|
||||
ReferenceItems.ToChange(),
|
||||
};
|
||||
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
|
|
@ -252,16 +250,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public async Task OnProjectChanged_AfterDispose_IgnoresUpdate()
|
||||
{
|
||||
// Arrange
|
||||
ReferenceItems.Item("c:\\nuget\\Microsoft.AspNetCore.Mvc.razor.dll");
|
||||
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "c:\\nuget\\Microsoft.AspNetCore.Mvc.razor.dll", new Dictionary<string, string>() },
|
||||
}),
|
||||
},
|
||||
ReferenceItems.ToChange(),
|
||||
};
|
||||
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
|
|
@ -300,16 +293,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public async Task OnProjectRenamed_RemovesHostProject_CopiesConfiguration()
|
||||
{
|
||||
// Arrange
|
||||
ReferenceItems.Item("c:\\nuget\\Microsoft.AspNetCore.Mvc.razor.dll");
|
||||
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "c:\\nuget\\Microsoft.AspNetCore.Mvc.razor.dll", new Dictionary<string, string>() },
|
||||
}),
|
||||
},
|
||||
ReferenceItems.ToChange(),
|
||||
};
|
||||
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
|
|
@ -361,11 +349,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
private class TestProjectSnapshotManager : DefaultProjectSnapshotManager
|
||||
{
|
||||
public TestProjectSnapshotManager(ForegroundDispatcher dispatcher, Workspace workspace)
|
||||
: base(dispatcher, Mock.Of<ErrorReporter>(), Mock.Of<ProjectSnapshotWorker>(), Array.Empty<ProjectSnapshotChangeTrigger>(), workspace)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void NotifyBackgroundWorker(ProjectSnapshotUpdateContext context)
|
||||
: base(dispatcher, Mock.Of<ErrorReporter>(), Array.Empty<ProjectSnapshotChangeTrigger>(), workspace)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
// 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.Collections.Immutable;
|
||||
using Microsoft.VisualStudio.ProjectSystem.Properties;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.VisualStudio.ProjectSystem
|
||||
{
|
||||
internal class ItemCollection
|
||||
{
|
||||
private readonly string _ruleName;
|
||||
private readonly Dictionary<string, Dictionary<string, string>> _items;
|
||||
|
||||
public ItemCollection(string ruleName)
|
||||
{
|
||||
_ruleName = ruleName;
|
||||
_items = new Dictionary<string, Dictionary<string, string>>();
|
||||
}
|
||||
|
||||
public void Item(string item)
|
||||
{
|
||||
Item(item, new Dictionary<string, string>());
|
||||
}
|
||||
|
||||
public void Item(string item, Dictionary<string, string> properties)
|
||||
{
|
||||
_items[item] = properties;
|
||||
}
|
||||
|
||||
public void RemoveItem(string item)
|
||||
{
|
||||
_items.Remove(item);
|
||||
}
|
||||
|
||||
public void Property(string item, string key)
|
||||
{
|
||||
_items[item][key] = null;
|
||||
}
|
||||
|
||||
public void Property(string item, string key, string value)
|
||||
{
|
||||
_items[item][key] = value;
|
||||
}
|
||||
|
||||
public TestProjectRuleSnapshot ToSnapshot()
|
||||
{
|
||||
return TestProjectRuleSnapshot.CreateItems(_ruleName, _items);
|
||||
}
|
||||
|
||||
public TestProjectChangeDescription ToChange()
|
||||
{
|
||||
return ToChange(new TestProjectRuleSnapshot(
|
||||
_ruleName,
|
||||
ImmutableDictionary<string, IImmutableDictionary<string, string>>.Empty,
|
||||
ImmutableDictionary<string, string>.Empty,
|
||||
ImmutableDictionary<NamedIdentity, IComparable>.Empty));
|
||||
}
|
||||
|
||||
public TestProjectChangeDescription ToChange(IProjectRuleSnapshot before)
|
||||
{
|
||||
Assert.Equal(_ruleName, before.RuleName);
|
||||
return new TestProjectChangeDescription(before, ToSnapshot());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
// 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.Collections.Immutable;
|
||||
using Microsoft.VisualStudio.ProjectSystem.Properties;
|
||||
|
||||
namespace Microsoft.VisualStudio.ProjectSystem
|
||||
{
|
||||
internal class PropertyCollection
|
||||
{
|
||||
private readonly string _ruleName;
|
||||
private readonly Dictionary<string, string> _properties;
|
||||
|
||||
public PropertyCollection(string ruleName)
|
||||
{
|
||||
_ruleName = ruleName;
|
||||
_properties = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
public void Property(string key)
|
||||
{
|
||||
_properties[key] = null;
|
||||
}
|
||||
|
||||
public void Property(string key, string value)
|
||||
{
|
||||
_properties[key] = value;
|
||||
}
|
||||
|
||||
public TestProjectRuleSnapshot ToSnapshot()
|
||||
{
|
||||
return TestProjectRuleSnapshot.CreateProperties(_ruleName, _properties);
|
||||
}
|
||||
|
||||
public TestProjectChangeDescription ToChange()
|
||||
{
|
||||
return ToChange(new TestProjectRuleSnapshot(
|
||||
_ruleName,
|
||||
ImmutableDictionary<string, IImmutableDictionary<string, string>>.Empty,
|
||||
ImmutableDictionary<string, string>.Empty,
|
||||
ImmutableDictionary<NamedIdentity, IComparable>.Empty));
|
||||
}
|
||||
|
||||
public TestProjectChangeDescription ToChange(IProjectRuleSnapshot before)
|
||||
{
|
||||
return new TestProjectChangeDescription(before, ToSnapshot());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +1,99 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.VisualStudio.ProjectSystem.Properties;
|
||||
|
||||
namespace Microsoft.VisualStudio.ProjectSystem
|
||||
{
|
||||
internal class TestProjectChangeDescription : IProjectChangeDescription
|
||||
{
|
||||
public string RuleName { get; set; }
|
||||
public TestProjectChangeDescription(IProjectRuleSnapshot before, IProjectRuleSnapshot after)
|
||||
{
|
||||
Before = before;
|
||||
After = after;
|
||||
|
||||
public TestProjectRuleSnapshot Before { get; set; }
|
||||
Difference = Diff.Create(before, after);
|
||||
}
|
||||
|
||||
public IProjectChangeDiff Difference { get; set; }
|
||||
public IProjectRuleSnapshot Before { get; }
|
||||
|
||||
public TestProjectRuleSnapshot After { get; set; }
|
||||
public IProjectChangeDiff Difference { get; }
|
||||
|
||||
IProjectRuleSnapshot IProjectChangeDescription.Before => Before;
|
||||
public IProjectRuleSnapshot After { get; }
|
||||
|
||||
IProjectChangeDiff IProjectChangeDescription.Difference => Difference;
|
||||
private class Diff : IProjectChangeDiff
|
||||
{
|
||||
public static Diff Create(IProjectRuleSnapshot before, IProjectRuleSnapshot after)
|
||||
{
|
||||
var addedItems = new HashSet<string>(after.Items.Keys);
|
||||
addedItems.ExceptWith(before.Items.Keys);
|
||||
|
||||
IProjectRuleSnapshot IProjectChangeDescription.After => After;
|
||||
var removedItems = new HashSet<string>(before.Items.Keys);
|
||||
removedItems.ExceptWith(after.Items.Keys);
|
||||
|
||||
// changed items must be present in both sets, but have different properties.
|
||||
var changedItems = new HashSet<string>(before.Items.Keys);
|
||||
changedItems.IntersectWith(after.Items.Keys);
|
||||
changedItems.RemoveWhere(key =>
|
||||
{
|
||||
var x = before.Items[key];
|
||||
var y = after.Items[key];
|
||||
|
||||
if (x.Count != y.Count)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var kvp in x)
|
||||
{
|
||||
if (!y.Contains(kvp))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
var changedProperties = new HashSet<string>(before.Properties.Keys);
|
||||
changedProperties.RemoveWhere(key =>
|
||||
{
|
||||
var x = before.Properties[key];
|
||||
var y = after.Properties[key];
|
||||
return object.Equals(x, y);
|
||||
});
|
||||
|
||||
return new Diff()
|
||||
{
|
||||
AddedItems = addedItems.ToImmutableHashSet(),
|
||||
RemovedItems = removedItems.ToImmutableHashSet(),
|
||||
ChangedItems = changedItems.ToImmutableHashSet(),
|
||||
|
||||
// We ignore renamed items.
|
||||
RenamedItems = ImmutableDictionary<string, string>.Empty,
|
||||
|
||||
ChangedProperties = changedProperties.ToImmutableHashSet(),
|
||||
};
|
||||
}
|
||||
|
||||
public IImmutableSet<string> AddedItems { get; private set; }
|
||||
|
||||
public IImmutableSet<string> RemovedItems { get; private set; }
|
||||
|
||||
public IImmutableSet<string> ChangedItems { get; private set; }
|
||||
|
||||
public IImmutableDictionary<string, string> RenamedItems { get; private set; }
|
||||
|
||||
public IImmutableSet<string> ChangedProperties { get; private set; }
|
||||
|
||||
public bool AnyChanges =>
|
||||
AddedItems.Count > 0 ||
|
||||
RemovedItems.Count > 0 ||
|
||||
ChangedItems.Count > 0 ||
|
||||
RenamedItems.Count > 0 ||
|
||||
ChangedProperties.Count > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -40,21 +40,11 @@ namespace Microsoft.VisualStudio.ProjectSystem
|
|||
DataSourceVersions = dataSourceVersions;
|
||||
}
|
||||
|
||||
public void SetProperty(string key, string value)
|
||||
{
|
||||
Properties = Properties.SetItem(key, value);
|
||||
}
|
||||
|
||||
public void SetItem(string key, Dictionary<string, string> values)
|
||||
{
|
||||
Items = Items.SetItem(key, values.ToImmutableDictionary());
|
||||
}
|
||||
|
||||
public string RuleName { get; }
|
||||
|
||||
public IImmutableDictionary<string, IImmutableDictionary<string, string>> Items { get; set; }
|
||||
public IImmutableDictionary<string, IImmutableDictionary<string, string>> Items { get; }
|
||||
|
||||
public IImmutableDictionary<string, string> Properties { get; set; }
|
||||
public IImmutableDictionary<string, string> Properties { get; }
|
||||
|
||||
public IImmutableDictionary<NamedIdentity, IComparable> DataSourceVersions { get; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
return new ProjectVersionedValue<IProjectSubscriptionUpdate>(
|
||||
value: new ProjectSubscriptionUpdate(
|
||||
projectChanges: descriptions.ToImmutableDictionary(d => d.RuleName, d => (IProjectChangeDescription)d),
|
||||
projectChanges: descriptions.ToImmutableDictionary(d => d.After.RuleName, d => (IProjectChangeDescription)d),
|
||||
projectConfiguration: ActiveConfiguredProject.ProjectConfiguration),
|
||||
dataSourceVersions: ImmutableDictionary<NamedIdentity, IComparable>.Empty);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,41 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
{
|
||||
public class VsSolutionUpdatesProjectSnapshotChangeTriggerTest
|
||||
{
|
||||
public VsSolutionUpdatesProjectSnapshotChangeTriggerTest()
|
||||
{
|
||||
SomeProject = new HostProject("c:\\SomeProject\\SomeProject.csproj", FallbackRazorConfiguration.MVC_1_0);
|
||||
SomeOtherProject = new HostProject("c:\\SomeOtherProject\\SomeOtherProject.csproj", FallbackRazorConfiguration.MVC_2_0);
|
||||
|
||||
Workspace = TestWorkspace.Create(w =>
|
||||
{
|
||||
SomeWorkspaceProject = w.AddProject(ProjectInfo.Create(
|
||||
ProjectId.CreateNewId(),
|
||||
VersionStamp.Create(),
|
||||
"SomeProject",
|
||||
"SomeProject",
|
||||
LanguageNames.CSharp,
|
||||
filePath: SomeProject.FilePath));
|
||||
|
||||
SomeOtherWorkspaceProject = w.AddProject(ProjectInfo.Create(
|
||||
ProjectId.CreateNewId(),
|
||||
VersionStamp.Create(),
|
||||
"SomeOtherProject",
|
||||
"SomeOtherProject",
|
||||
LanguageNames.CSharp,
|
||||
filePath: SomeOtherProject.FilePath));
|
||||
});
|
||||
}
|
||||
|
||||
private HostProject SomeProject { get; }
|
||||
|
||||
private HostProject SomeOtherProject { get; }
|
||||
|
||||
private Project SomeWorkspaceProject { get; set; }
|
||||
|
||||
private Project SomeOtherWorkspaceProject { get; set; }
|
||||
|
||||
private Workspace Workspace { get; }
|
||||
|
||||
[Fact]
|
||||
public void Initialize_AttachesEventSink()
|
||||
{
|
||||
|
|
@ -38,10 +73,10 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void UpdateProjectCfg_Done_KnownProject_Invokes_ProjectBuildComplete()
|
||||
public void UpdateProjectCfg_Done_KnownProject_Invokes_WorkspaceProjectChanged()
|
||||
{
|
||||
// Arrange
|
||||
var expectedProjectPath = "Path/To/Project";
|
||||
var expectedProjectPath = SomeProject.FilePath;
|
||||
|
||||
uint cookie;
|
||||
var buildManager = new Mock<IVsSolutionBuildManager>(MockBehavior.Strict);
|
||||
|
|
@ -57,16 +92,19 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
|
||||
var projectSnapshots = new[]
|
||||
{
|
||||
Mock.Of<ProjectSnapshot>(p => p.FilePath == expectedProjectPath && p.HostProject == new HostProject(expectedProjectPath, RazorConfiguration.Default)),
|
||||
Mock.Of<ProjectSnapshot>(p => p.FilePath == "Test2.csproj" && p.HostProject == new HostProject("Test2.csproj", RazorConfiguration.Default)),
|
||||
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeProject, SomeWorkspaceProject)),
|
||||
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeOtherProject, SomeOtherWorkspaceProject)),
|
||||
};
|
||||
|
||||
var called = false;
|
||||
var projectManager = new Mock<ProjectSnapshotManagerBase>();
|
||||
projectManager.SetupGet(p => p.Projects).Returns(projectSnapshots);
|
||||
projectManager.SetupGet(p => p.Workspace).Returns(Workspace);
|
||||
projectManager
|
||||
.Setup(p => p.HostProjectBuildComplete(It.IsAny<HostProject>()))
|
||||
.Callback<HostProject>(c =>
|
||||
.Setup(p => p.GetLoadedProject(expectedProjectPath))
|
||||
.Returns(projectSnapshots[0]);
|
||||
projectManager
|
||||
.Setup(p => p.WorkspaceProjectChanged(It.IsAny<Project>()))
|
||||
.Callback<Project>(c =>
|
||||
{
|
||||
called = true;
|
||||
Assert.Equal(expectedProjectPath, c.FilePath);
|
||||
|
|
@ -83,7 +121,50 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void UpdateProjectCfg_Done_UnknownProject_DoesNotInvoke_ProjectBuildComplete()
|
||||
public void UpdateProjectCfg_Done_WithoutWorkspaceProject_DoesNotInvoke_WorkspaceProjectChanged()
|
||||
{
|
||||
// Arrange
|
||||
var expectedProjectPath = SomeProject.FilePath;
|
||||
|
||||
uint cookie;
|
||||
var buildManager = new Mock<IVsSolutionBuildManager>(MockBehavior.Strict);
|
||||
buildManager
|
||||
.Setup(b => b.AdviseUpdateSolutionEvents(It.IsAny<VsSolutionUpdatesProjectSnapshotChangeTrigger>(), out cookie))
|
||||
.Returns(VSConstants.S_OK);
|
||||
|
||||
var services = new Mock<IServiceProvider>();
|
||||
services.Setup(s => s.GetService(It.Is<Type>(f => f == typeof(SVsSolutionBuildManager)))).Returns(buildManager.Object);
|
||||
|
||||
var projectService = new Mock<TextBufferProjectService>();
|
||||
projectService.Setup(p => p.GetProjectPath(It.IsAny<IVsHierarchy>())).Returns(expectedProjectPath);
|
||||
|
||||
var projectSnapshots = new[]
|
||||
{
|
||||
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeProject, null)),
|
||||
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeOtherProject, SomeOtherWorkspaceProject)),
|
||||
};
|
||||
|
||||
var projectManager = new Mock<ProjectSnapshotManagerBase>();
|
||||
projectManager.SetupGet(p => p.Workspace).Returns(Workspace);
|
||||
projectManager
|
||||
.Setup(p => p.GetLoadedProject(expectedProjectPath))
|
||||
.Returns(projectSnapshots[0]);
|
||||
projectManager
|
||||
.Setup(p => p.WorkspaceProjectChanged(It.IsAny<Project>()))
|
||||
.Callback<Project>(c =>
|
||||
{
|
||||
throw new InvalidOperationException("This should not be called.");
|
||||
});
|
||||
|
||||
var trigger = new VsSolutionUpdatesProjectSnapshotChangeTrigger(services.Object, projectService.Object);
|
||||
trigger.Initialize(projectManager.Object);
|
||||
|
||||
// Act & Assert - Does not throw
|
||||
trigger.UpdateProjectCfg_Done(Mock.Of<IVsHierarchy>(), Mock.Of<IVsCfg>(), Mock.Of<IVsCfg>(), 0, 0, 0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UpdateProjectCfg_Done_UnknownProject_DoesNotInvoke_WorkspaceProjectChanged()
|
||||
{
|
||||
// Arrange
|
||||
var expectedProjectPath = "Path/To/Project";
|
||||
|
|
@ -102,15 +183,18 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
|
||||
var projectSnapshots = new[]
|
||||
{
|
||||
Mock.Of<ProjectSnapshot>(p => p.FilePath == "Path/To/AnotherProject" && p.HostProject == new HostProject("Path/To/AnotherProject", RazorConfiguration.Default)),
|
||||
Mock.Of<ProjectSnapshot>(p => p.FilePath == "Path/To/DifferenProject" && p.HostProject == new HostProject("Path/To/DifferenProject", RazorConfiguration.Default)),
|
||||
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeProject, SomeWorkspaceProject)),
|
||||
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeOtherProject, SomeOtherWorkspaceProject)),
|
||||
};
|
||||
|
||||
var projectManager = new Mock<ProjectSnapshotManagerBase>();
|
||||
projectManager.SetupGet(p => p.Projects).Returns(projectSnapshots);
|
||||
projectManager.SetupGet(p => p.Workspace).Returns(Workspace);
|
||||
projectManager
|
||||
.Setup(p => p.HostProjectBuildComplete(It.IsAny<HostProject>()))
|
||||
.Callback<HostProject>(c =>
|
||||
.Setup(p => p.GetLoadedProject(expectedProjectPath))
|
||||
.Returns((ProjectSnapshot)null);
|
||||
projectManager
|
||||
.Setup(p => p.WorkspaceProjectChanged(It.IsAny<Project>()))
|
||||
.Callback<Project>(c =>
|
||||
{
|
||||
throw new InvalidOperationException("This should not be called.");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,6 +4,12 @@
|
|||
<TargetFramework>net461</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Microsoft.CodeAnalysis.Razor.Workspaces.Test\Shared\**\*.cs">
|
||||
<Link>Shared\%(RecursiveDir)%(FileName)%(Extension)</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -10,29 +10,71 @@ using MonoDevelop.Projects;
|
|||
using Moq;
|
||||
using Xunit;
|
||||
using Project = Microsoft.CodeAnalysis.Project;
|
||||
using Workspace = Microsoft.CodeAnalysis.Workspace;
|
||||
|
||||
namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor
|
||||
{
|
||||
public class ProjectBuildChangeTriggerTest : ForegroundDispatcherTestBase
|
||||
{
|
||||
public ProjectBuildChangeTriggerTest()
|
||||
{
|
||||
SomeProject = new HostProject("c:\\SomeProject\\SomeProject.csproj", FallbackRazorConfiguration.MVC_1_0);
|
||||
SomeOtherProject = new HostProject("c:\\SomeOtherProject\\SomeOtherProject.csproj", FallbackRazorConfiguration.MVC_2_0);
|
||||
|
||||
Workspace = TestWorkspace.Create(w =>
|
||||
{
|
||||
SomeWorkspaceProject = w.AddProject(ProjectInfo.Create(
|
||||
ProjectId.CreateNewId(),
|
||||
VersionStamp.Create(),
|
||||
"SomeProject",
|
||||
"SomeProject",
|
||||
LanguageNames.CSharp,
|
||||
filePath: SomeProject.FilePath));
|
||||
|
||||
SomeOtherWorkspaceProject = w.AddProject(ProjectInfo.Create(
|
||||
ProjectId.CreateNewId(),
|
||||
VersionStamp.Create(),
|
||||
"SomeOtherProject",
|
||||
"SomeOtherProject",
|
||||
LanguageNames.CSharp,
|
||||
filePath: SomeOtherProject.FilePath));
|
||||
});
|
||||
}
|
||||
|
||||
private HostProject SomeProject { get; }
|
||||
|
||||
private HostProject SomeOtherProject { get; }
|
||||
|
||||
private Project SomeWorkspaceProject { get; set; }
|
||||
|
||||
private Project SomeOtherWorkspaceProject { get; set; }
|
||||
|
||||
private Workspace Workspace { get; }
|
||||
|
||||
[ForegroundFact]
|
||||
public void ProjectOperations_EndBuild_Invokes_ProjectBuildComplete()
|
||||
public void ProjectOperations_EndBuild_Invokes_WorkspaceProjectChanged()
|
||||
{
|
||||
// Arrange
|
||||
var args = new BuildEventArgs(monitor: null, success: true);
|
||||
var expectedProjectPath = "Path/To/Project";
|
||||
var expectedProjectPath = SomeProject.FilePath;
|
||||
var projectService = CreateProjectService(expectedProjectPath);
|
||||
|
||||
var args = new BuildEventArgs(monitor: null, success: true);
|
||||
|
||||
var projectSnapshots = new[]
|
||||
{
|
||||
Mock.Of<ProjectSnapshot>(p => p.FilePath == expectedProjectPath && p.HostProject == new HostProject(expectedProjectPath, RazorConfiguration.Default)),
|
||||
Mock.Of<ProjectSnapshot>(p => p.FilePath == "Test2.csproj" && p.HostProject == new HostProject("Test2.csproj", RazorConfiguration.Default)),
|
||||
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeProject, SomeWorkspaceProject)),
|
||||
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeOtherProject, SomeOtherWorkspaceProject)),
|
||||
};
|
||||
|
||||
var projectManager = new Mock<ProjectSnapshotManagerBase>(MockBehavior.Strict);
|
||||
projectManager.SetupGet(p => p.Projects).Returns(projectSnapshots);
|
||||
projectManager.SetupGet(p => p.Workspace).Returns(Workspace);
|
||||
projectManager
|
||||
.Setup(p => p.HostProjectBuildComplete(It.IsAny<HostProject>()))
|
||||
.Callback<HostProject>(c => Assert.Equal(expectedProjectPath, c.FilePath));
|
||||
.Setup(p => p.GetLoadedProject(SomeProject.FilePath))
|
||||
.Returns(projectSnapshots[0]);
|
||||
projectManager
|
||||
.Setup(p => p.WorkspaceProjectChanged(It.IsAny<Project>()))
|
||||
.Callback<Project>(c => Assert.Equal(expectedProjectPath, c.FilePath));
|
||||
|
||||
var trigger = new ProjectBuildChangeTrigger(Dispatcher, projectService, projectManager.Object);
|
||||
|
||||
// Act
|
||||
|
|
@ -42,21 +84,56 @@ namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor
|
|||
projectManager.VerifyAll();
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void ProjectOperations_EndBuild_ProjectWithoutWorkspaceProject_Noops()
|
||||
{
|
||||
// Arrange
|
||||
var projectService = CreateProjectService(SomeProject.FilePath);
|
||||
|
||||
var args = new BuildEventArgs(monitor: null, success: true);
|
||||
var projectSnapshots = new[]
|
||||
{
|
||||
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeProject, null)),
|
||||
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeOtherProject, SomeOtherWorkspaceProject)),
|
||||
};
|
||||
|
||||
var projectManager = new Mock<ProjectSnapshotManagerBase>();
|
||||
projectManager.SetupGet(p => p.Workspace).Returns(Workspace);
|
||||
projectManager
|
||||
.Setup(p => p.GetLoadedProject(SomeProject.FilePath))
|
||||
.Returns(projectSnapshots[0]);
|
||||
projectManager
|
||||
.Setup(p => p.WorkspaceProjectChanged(It.IsAny<Project>()))
|
||||
.Throws<InvalidOperationException>();
|
||||
|
||||
var trigger = new ProjectBuildChangeTrigger(Dispatcher, projectService, projectManager.Object);
|
||||
|
||||
// Act & Assert
|
||||
trigger.ProjectOperations_EndBuild(null, args);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void ProjectOperations_EndBuild_UntrackedProject_Noops()
|
||||
{
|
||||
// Arrange
|
||||
var args = new BuildEventArgs(monitor: null, success: true);
|
||||
var projectService = CreateProjectService("Path/To/Project");
|
||||
|
||||
var args = new BuildEventArgs(monitor: null, success: true);
|
||||
var projectSnapshots = new[]
|
||||
{
|
||||
Mock.Of<ProjectSnapshot>(p => p.FilePath == "Path/To/AnotherProject" && p.HostProject == new HostProject("Path/To/AnotherProject", RazorConfiguration.Default)),
|
||||
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeProject, null)),
|
||||
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeOtherProject, SomeOtherWorkspaceProject)),
|
||||
};
|
||||
|
||||
var projectManager = new Mock<ProjectSnapshotManagerBase>();
|
||||
projectManager.SetupGet(p => p.Projects).Returns(projectSnapshots);
|
||||
projectManager.SetupGet(p => p.Workspace).Returns(Workspace);
|
||||
projectManager
|
||||
.Setup(p => p.HostProjectBuildComplete(It.IsAny<HostProject>()))
|
||||
.Setup(p => p.GetLoadedProject(SomeProject.FilePath))
|
||||
.Returns(projectSnapshots[0]);
|
||||
projectManager
|
||||
.Setup(p => p.WorkspaceProjectChanged(It.IsAny<Project>()))
|
||||
.Throws<InvalidOperationException>();
|
||||
|
||||
var trigger = new ProjectBuildChangeTrigger(Dispatcher, projectService, projectManager.Object);
|
||||
|
||||
// Act & Assert
|
||||
|
|
@ -100,11 +177,5 @@ namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor
|
|||
projectService.Setup(p => p.IsSupportedProject(null)).Returns(true);
|
||||
return projectService.Object;
|
||||
}
|
||||
|
||||
private static AdhocWorkspace CreateProjectInWorkspace(AdhocWorkspace workspace, string name, string path)
|
||||
{
|
||||
workspace.AddProject(ProjectInfo.Create(ProjectId.CreateNewId(), new VersionStamp(), name, "TestAssembly", LanguageNames.CSharp, filePath: path));
|
||||
return workspace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,13 +73,11 @@
|
|||
<Compile Include="DocumentInfo\RazorDocumentInfoWindowControl.xaml.cs">
|
||||
<DependentUpon>RazorDocumentInfoWindowControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="RazorInfo\AssemblyViewModel.cs" />
|
||||
<Compile Include="Behaviors\ItemSelectedBehavior.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Properties\BindingRedirectAttributes.cs" />
|
||||
<Compile Include="RazorInfo\DirectiveViewModel.cs" />
|
||||
<Compile Include="RazorInfo\DocumentInfoViewModel.cs" />
|
||||
<Compile Include="RazorInfo\DocumentViewModel.cs" />
|
||||
<Compile Include="RazorInfo\DirectiveDescriptorViewModel.cs" />
|
||||
<Compile Include="RazorInfo\DocumentSnapshotViewModel.cs" />
|
||||
<Compile Include="NotifyPropertyChanged.cs" />
|
||||
<Compile Include="RazorInfo\ProjectSnapshotViewModel.cs" />
|
||||
<Compile Include="RazorInfo\ProjectViewModel.cs" />
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@ using Microsoft.AspNetCore.Razor.Language;
|
|||
|
||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||
{
|
||||
public class DirectiveViewModel : NotifyPropertyChanged
|
||||
public class DirectiveDescriptorViewModel : NotifyPropertyChanged
|
||||
{
|
||||
private readonly DirectiveDescriptor _directive;
|
||||
|
||||
internal DirectiveViewModel(DirectiveDescriptor directive)
|
||||
internal DirectiveDescriptorViewModel(DirectiveDescriptor directive)
|
||||
{
|
||||
_directive = directive;
|
||||
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
// 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.
|
||||
|
||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
using Microsoft.VisualStudio.LanguageServices.Razor;
|
||||
|
||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||
{
|
||||
public class DocumentInfoViewModel : NotifyPropertyChanged
|
||||
{
|
||||
private RazorEngineDocument _document;
|
||||
|
||||
internal DocumentInfoViewModel(RazorEngineDocument document)
|
||||
{
|
||||
_document = document;
|
||||
}
|
||||
|
||||
public string Text => _document.Text;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -2,22 +2,23 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||
{
|
||||
public class AssemblyViewModel : NotifyPropertyChanged
|
||||
public class DocumentSnapshotViewModel : NotifyPropertyChanged
|
||||
{
|
||||
private readonly ProjectExtensibilityAssembly _assembly;
|
||||
|
||||
internal AssemblyViewModel(ProjectExtensibilityAssembly assembly)
|
||||
internal DocumentSnapshotViewModel(DocumentSnapshot document)
|
||||
{
|
||||
_assembly = assembly;
|
||||
|
||||
Name = _assembly.Identity.GetDisplayName();
|
||||
Document = document;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
internal DocumentSnapshot Document { get; }
|
||||
|
||||
public string FilePath => Document.FilePath;
|
||||
|
||||
public string TargetPath => Document.TargetPath;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
// 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.
|
||||
|
||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
|
||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||
{
|
||||
public class DocumentViewModel : NotifyPropertyChanged
|
||||
{
|
||||
public DocumentViewModel(string filePath)
|
||||
{
|
||||
FilePath = filePath;
|
||||
}
|
||||
|
||||
public string FilePath { get; }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -3,16 +3,18 @@
|
|||
|
||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows;
|
||||
|
||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||
{
|
||||
public class ProjectInfoViewModel : NotifyPropertyChanged
|
||||
{
|
||||
private ObservableCollection<DirectiveViewModel> _directives;
|
||||
private ObservableCollection<DocumentViewModel> _documents;
|
||||
private ObservableCollection<DirectiveDescriptorViewModel> _directives;
|
||||
private ObservableCollection<DocumentSnapshotViewModel> _documents;
|
||||
private ObservableCollection<TagHelperViewModel> _tagHelpers;
|
||||
private bool _tagHelpersLoading;
|
||||
|
||||
public ObservableCollection<DirectiveViewModel> Directives
|
||||
public ObservableCollection<DirectiveDescriptorViewModel> Directives
|
||||
{
|
||||
get { return _directives; }
|
||||
set
|
||||
|
|
@ -22,7 +24,7 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<DocumentViewModel> Documents
|
||||
public ObservableCollection<DocumentSnapshotViewModel> Documents
|
||||
{
|
||||
get { return _documents; }
|
||||
set
|
||||
|
|
@ -41,6 +43,20 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public bool TagHelpersLoading
|
||||
{
|
||||
get { return _tagHelpersLoading; }
|
||||
set
|
||||
{
|
||||
_tagHelpersLoading = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(TagHelperProgressVisibility));
|
||||
}
|
||||
}
|
||||
|
||||
public Visibility TagHelperProgressVisibility => TagHelpersLoading ? Visibility.Visible : Visibility.Hidden;
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
|
|
@ -16,12 +18,9 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
Project = project;
|
||||
|
||||
Id = project.WorkspaceProject?.Id;
|
||||
Properties = new ObservableCollection<PropertyViewModel>()
|
||||
{
|
||||
new PropertyViewModel("Razor Language Version", project.Configuration?.LanguageVersion.ToString()),
|
||||
new PropertyViewModel("Configuration Name", $"{project.Configuration?.ConfigurationName} ({project.Configuration?.GetType().Name ?? "unknown"})"),
|
||||
new PropertyViewModel("Workspace Project", project.WorkspaceProject?.Name)
|
||||
};
|
||||
Properties = new ObservableCollection<PropertyViewModel>();
|
||||
|
||||
InitializeProperties();
|
||||
}
|
||||
|
||||
internal ProjectSnapshot Project { get; }
|
||||
|
|
@ -31,6 +30,26 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
public ProjectId Id { get; }
|
||||
|
||||
public ObservableCollection<PropertyViewModel> Properties { get; }
|
||||
|
||||
private void InitializeProperties()
|
||||
{
|
||||
Properties.Clear();
|
||||
|
||||
Properties.Add(new PropertyViewModel("Language Version", Project.Configuration?.LanguageVersion.ToString()));
|
||||
Properties.Add(new PropertyViewModel("Configuration", FormatConfiguration(Project)));
|
||||
Properties.Add(new PropertyViewModel("Extensions", FormatExtensions(Project)));
|
||||
Properties.Add(new PropertyViewModel("Workspace Project", Project.WorkspaceProject?.Name));
|
||||
}
|
||||
|
||||
private static string FormatConfiguration(ProjectSnapshot project)
|
||||
{
|
||||
return $"{project.Configuration.ConfigurationName} ({project.Configuration.GetType().Name})";
|
||||
}
|
||||
|
||||
private static string FormatExtensions(ProjectSnapshot project)
|
||||
{
|
||||
return $"{string.Join(", ", project.Configuration.Extensions.Select(e => e.ExtensionName))}";
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue