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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Microsoft.Extensions.Internal;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language
|
namespace Microsoft.AspNetCore.Razor.Language
|
||||||
{
|
{
|
||||||
public abstract class RazorConfiguration
|
public abstract class RazorConfiguration : IEquatable<RazorConfiguration>
|
||||||
{
|
{
|
||||||
public static readonly RazorConfiguration Default = new DefaultRazorConfiguration(
|
public static readonly RazorConfiguration Default = new DefaultRazorConfiguration(
|
||||||
RazorLanguageVersion.Latest,
|
RazorLanguageVersion.Latest,
|
||||||
|
|
@ -43,6 +44,58 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
|
|
||||||
public abstract RazorLanguageVersion LanguageVersion { get; }
|
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
|
private class DefaultRazorConfiguration : RazorConfiguration
|
||||||
{
|
{
|
||||||
public DefaultRazorConfiguration(
|
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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
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
|
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;
|
State = state;
|
||||||
WorkspaceProject = workspaceProject; // Might be null
|
|
||||||
|
_lock = new object();
|
||||||
FilePath = hostProject.FilePath;
|
_documents = new Dictionary<string, DefaultDocumentSnapshot>(FilePathComparer.Instance);
|
||||||
Version = version ?? VersionStamp.Default;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private DefaultProjectSnapshot(HostProject hostProject, DefaultProjectSnapshot other)
|
public ProjectState State { get; }
|
||||||
{
|
|
||||||
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 override RazorConfiguration Configuration => HostProject.Configuration;
|
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 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>();
|
public override DocumentSnapshot GetDocument(string filePath)
|
||||||
|
|
||||||
// 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()
|
|
||||||
{
|
{
|
||||||
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);
|
results = null;
|
||||||
}
|
return false;
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
@ -31,15 +30,14 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
private readonly ErrorReporter _errorReporter;
|
private readonly ErrorReporter _errorReporter;
|
||||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||||
private readonly ProjectSnapshotChangeTrigger[] _triggers;
|
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(
|
public DefaultProjectSnapshotManager(
|
||||||
ForegroundDispatcher foregroundDispatcher,
|
ForegroundDispatcher foregroundDispatcher,
|
||||||
ErrorReporter errorReporter,
|
ErrorReporter errorReporter,
|
||||||
ProjectSnapshotWorker worker,
|
|
||||||
IEnumerable<ProjectSnapshotChangeTrigger> triggers,
|
IEnumerable<ProjectSnapshotChangeTrigger> triggers,
|
||||||
Workspace workspace)
|
Workspace workspace)
|
||||||
{
|
{
|
||||||
|
|
@ -53,11 +51,6 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
throw new ArgumentNullException(nameof(errorReporter));
|
throw new ArgumentNullException(nameof(errorReporter));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (worker == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(worker));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (triggers == null)
|
if (triggers == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(triggers));
|
throw new ArgumentNullException(nameof(triggers));
|
||||||
|
|
@ -70,13 +63,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
||||||
_foregroundDispatcher = foregroundDispatcher;
|
_foregroundDispatcher = foregroundDispatcher;
|
||||||
_errorReporter = errorReporter;
|
_errorReporter = errorReporter;
|
||||||
_worker = worker;
|
|
||||||
_triggers = triggers.ToArray();
|
_triggers = triggers.ToArray();
|
||||||
Workspace = workspace;
|
Workspace = workspace;
|
||||||
|
|
||||||
_projects = new Dictionary<string, DefaultProjectSnapshot>(FilePathComparer.Instance);
|
_projects = new Dictionary<string, Entry>(FilePathComparer.Instance);
|
||||||
|
|
||||||
_workerQueue = new ProjectSnapshotWorkerQueue(_foregroundDispatcher, this, worker);
|
|
||||||
|
|
||||||
for (var i = 0; i < _triggers.Length; i++)
|
for (var i = 0; i < _triggers.Length; i++)
|
||||||
{
|
{
|
||||||
|
|
@ -89,43 +79,109 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
_foregroundDispatcher.AssertForegroundThread();
|
_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 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();
|
_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.
|
entry.Snapshot = new DefaultProjectSnapshot(entry.State);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is an update to the project's computed values, so everything should be overwritten
|
return entry.Snapshot;
|
||||||
var snapshot = original.WithComputedUpdate(update);
|
}
|
||||||
_projects[update.WorkspaceProject.FilePath] = 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
|
_projects[hostProject.FilePath] = new Entry(state);
|
||||||
// the background. We need to trigger the background work to asynchronously compute the effect of the updates.
|
NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, ProjectChangeKind.DocumentsChanged));
|
||||||
NotifyBackgroundWorker(snapshot.CreateUpdateContext());
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
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.
|
// So if possible find a WorkspaceProject.
|
||||||
var workspaceProject = GetWorkspaceProject(hostProject.FilePath);
|
var workspaceProject = GetWorkspaceProject(hostProject.FilePath);
|
||||||
|
|
||||||
var snapshot = new DefaultProjectSnapshot(hostProject, workspaceProject);
|
var state = new ProjectState(Workspace.Services, hostProject, workspaceProject);
|
||||||
_projects[hostProject.FilePath] = snapshot;
|
_projects[hostProject.FilePath] = new Entry(state);
|
||||||
|
|
||||||
if (snapshot.IsInitialized && snapshot.IsDirty)
|
|
||||||
{
|
|
||||||
// Start computing background state if the project is fully initialized.
|
|
||||||
NotifyBackgroundWorker(snapshot.CreateUpdateContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need to notify listeners about every project add.
|
// 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)
|
public override void HostProjectChanged(HostProject hostProject)
|
||||||
|
|
@ -171,22 +221,17 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
||||||
_foregroundDispatcher.AssertForegroundThread();
|
_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
|
var state = entry.State.WithHostProject(hostProject);
|
||||||
// underlying project is newer.
|
|
||||||
var snapshot = original.WithHostProject(hostProject);
|
|
||||||
_projects[hostProject.FilePath] = snapshot;
|
|
||||||
|
|
||||||
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.
|
_projects[hostProject.FilePath] = new Entry(state);
|
||||||
NotifyBackgroundWorker(snapshot.CreateUpdateContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify listeners right away because if the HostProject changes then it's likely that the Razor
|
NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, ProjectChangeKind.ProjectChanged));
|
||||||
// configuration changed.
|
}
|
||||||
NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.Changed));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -204,37 +249,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
_projects.Remove(hostProject.FilePath);
|
_projects.Remove(hostProject.FilePath);
|
||||||
|
|
||||||
// We need to notify listeners about every project removal.
|
// We need to notify listeners about every project removal.
|
||||||
NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.Removed));
|
NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, ProjectChangeKind.ProjectRemoved));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -254,25 +269,16 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
||||||
// The WorkspaceProject initialization never triggers a "Project Add" from out point of view, we
|
// The WorkspaceProject initialization never triggers a "Project Add" from out point of view, we
|
||||||
// only care if the new WorkspaceProject matches an existing HostProject.
|
// 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
|
// 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.
|
// found one in the past just ignore this one.
|
||||||
if (original.WorkspaceProject == null)
|
if (entry.State.WorkspaceProject == null)
|
||||||
{
|
{
|
||||||
var snapshot = original.WithWorkspaceProject(workspaceProject);
|
var state = entry.State.WithWorkspaceProject(workspaceProject);
|
||||||
_projects[workspaceProject.FilePath] = snapshot;
|
_projects[workspaceProject.FilePath] = new Entry(state);
|
||||||
|
|
||||||
if (snapshot.IsInitialized && snapshot.IsDirty)
|
NotifyListeners(new ProjectChangeEventArgs(workspaceProject.FilePath, ProjectChangeKind.ProjectChanged));
|
||||||
{
|
|
||||||
// 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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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
|
// 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.
|
// in a single workspace project. Just use the one that showed up first.
|
||||||
if (_projects.TryGetValue(workspaceProject.FilePath, out var original) &&
|
if (_projects.TryGetValue(workspaceProject.FilePath, out var entry) &&
|
||||||
(original.WorkspaceProject == null ||
|
(entry.State.WorkspaceProject == null || entry.State.WorkspaceProject.Id == workspaceProject.Id))
|
||||||
original.WorkspaceProject.Id == workspaceProject.Id))
|
|
||||||
{
|
{
|
||||||
// Doing an update to the project should keep computed values, but mark the project as dirty if the
|
var state = entry.State.WithWorkspaceProject(workspaceProject);
|
||||||
// underlying project is newer.
|
|
||||||
var snapshot = original.WithWorkspaceProject(workspaceProject);
|
// WorkspaceProject updates can no-op. This can be the case if a build is triggered, but we've
|
||||||
_projects[workspaceProject.FilePath] = snapshot;
|
// already seen the update.
|
||||||
|
if (!object.ReferenceEquals(state, 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
|
_projects[workspaceProject.FilePath] = new Entry(state);
|
||||||
// need to trigger the background work to asynchronously compute the effect of the updates.
|
|
||||||
NotifyBackgroundWorker(snapshot.CreateUpdateContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (snapshot.HaveTagHelpersChanged(original))
|
NotifyListeners(new ProjectChangeEventArgs(workspaceProject.FilePath, ProjectChangeKind.ProjectChanged));
|
||||||
{
|
|
||||||
NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.TagHelpersChanged));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -330,16 +329,16 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
return;
|
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
|
// 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.
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultProjectSnapshot snapshot;
|
ProjectState state;
|
||||||
|
|
||||||
// So if the WorkspaceProject got removed, we should double check to make sure that there aren't others
|
// 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.
|
// 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)
|
if (otherWorkspaceProject != null && otherWorkspaceProject.Id != workspaceProject.Id)
|
||||||
{
|
{
|
||||||
// OK there's another WorkspaceProject, use that.
|
// OK there's another WorkspaceProject, use that.
|
||||||
//
|
state = entry.State.WithWorkspaceProject(otherWorkspaceProject);
|
||||||
// Doing an update to the project should keep computed values, but mark the project as dirty if the
|
_projects[otherWorkspaceProject.FilePath] = new Entry(state);
|
||||||
// underlying project is newer.
|
|
||||||
snapshot = original.WithWorkspaceProject(otherWorkspaceProject);
|
|
||||||
_projects[workspaceProject.FilePath] = snapshot;
|
|
||||||
|
|
||||||
if (snapshot.IsInitialized && snapshot.IsDirty)
|
NotifyListeners(new ProjectChangeEventArgs(otherWorkspaceProject.FilePath, ProjectChangeKind.ProjectChanged));
|
||||||
{
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state = entry.State.WithWorkspaceProject(null);
|
||||||
|
_projects[workspaceProject.FilePath] = new Entry(state);
|
||||||
|
|
||||||
snapshot = original.RemoveWorkspaceProject();
|
// Notify listeners of a change because we've removed computed state.
|
||||||
_projects[workspaceProject.FilePath] = snapshot;
|
NotifyListeners(new ProjectChangeEventArgs(workspaceProject.FilePath, ProjectChangeKind.ProjectChanged));
|
||||||
|
}
|
||||||
// Notify listeners of a change because we've removed computed state.
|
|
||||||
NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.Changed));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -401,8 +389,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
throw new ArgumentNullException(nameof(exception));
|
throw new ArgumentNullException(nameof(exception));
|
||||||
}
|
}
|
||||||
|
|
||||||
var project = hostProject?.FilePath == null ? null : this.GetProjectWithFilePath(hostProject.FilePath);
|
var snapshot = hostProject?.FilePath == null ? null : GetLoadedProject(hostProject.FilePath);
|
||||||
_errorReporter.ReportError(exception, project);
|
_errorReporter.ReportError(exception, snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void ReportError(Exception exception, Project workspaceProject)
|
public override void ReportError(Exception exception, Project workspaceProject)
|
||||||
|
|
@ -411,7 +399,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(exception));
|
throw new ArgumentNullException(nameof(exception));
|
||||||
}
|
}
|
||||||
|
|
||||||
_errorReporter.ReportError(exception, workspaceProject);
|
_errorReporter.ReportError(exception, workspaceProject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -440,14 +428,6 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
return null;
|
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
|
// virtual so it can be overridden in tests
|
||||||
protected virtual void NotifyListeners(ProjectChangeEventArgs e)
|
protected virtual void NotifyListeners(ProjectChangeEventArgs e)
|
||||||
{
|
{
|
||||||
|
|
@ -459,5 +439,16 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
handler(this, e);
|
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(
|
return new DefaultProjectSnapshotManager(
|
||||||
_foregroundDispatcher,
|
_foregroundDispatcher,
|
||||||
languageServices.WorkspaceServices.GetRequiredService<ErrorReporter>(),
|
languageServices.WorkspaceServices.GetRequiredService<ErrorReporter>(),
|
||||||
languageServices.GetRequiredService<ProjectSnapshotWorker>(),
|
_triggers,
|
||||||
_triggers,
|
|
||||||
languageServices.WorkspaceServices.Workspace);
|
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
|
internal class ProjectChangeEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public ProjectChangeEventArgs(ProjectSnapshot project, ProjectChangeKind kind)
|
public ProjectChangeEventArgs(string projectFilePath, ProjectChangeKind kind)
|
||||||
{
|
{
|
||||||
Project = project;
|
ProjectFilePath = projectFilePath;
|
||||||
Kind = kind;
|
Kind = kind;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProjectSnapshot Project { get; }
|
public string ProjectFilePath { get; }
|
||||||
|
|
||||||
public ProjectChangeKind Kind { get; }
|
public ProjectChangeKind Kind { get; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
{
|
{
|
||||||
internal enum ProjectChangeKind
|
internal enum ProjectChangeKind
|
||||||
{
|
{
|
||||||
Added,
|
ProjectAdded,
|
||||||
Removed,
|
ProjectRemoved,
|
||||||
Changed,
|
ProjectChanged,
|
||||||
TagHelpersChanged,
|
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.
|
// 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.
|
// 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.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
@ -11,16 +11,22 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
{
|
{
|
||||||
public abstract RazorConfiguration Configuration { get; }
|
public abstract RazorConfiguration Configuration { get; }
|
||||||
|
|
||||||
|
public abstract IEnumerable<string> DocumentFilePaths { get; }
|
||||||
|
|
||||||
public abstract string FilePath { get; }
|
public abstract string FilePath { get; }
|
||||||
|
|
||||||
public abstract bool IsInitialized { get; }
|
public abstract bool IsInitialized { get; }
|
||||||
|
|
||||||
public abstract IReadOnlyList<TagHelperDescriptor> TagHelpers { get; }
|
|
||||||
|
|
||||||
public abstract VersionStamp Version { get; }
|
public abstract VersionStamp Version { get; }
|
||||||
|
|
||||||
public abstract Project WorkspaceProject { 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 event EventHandler<ProjectChangeEventArgs> Changed;
|
||||||
|
|
||||||
public abstract IReadOnlyList<ProjectSnapshot> Projects { get; }
|
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 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);
|
public abstract void HostProjectAdded(HostProject hostProject);
|
||||||
|
|
||||||
|
|
@ -17,8 +19,6 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
||||||
public abstract void HostProjectRemoved(HostProject hostProject);
|
public abstract void HostProjectRemoved(HostProject hostProject);
|
||||||
|
|
||||||
public abstract void HostProjectBuildComplete(HostProject hostProject);
|
|
||||||
|
|
||||||
public abstract void WorkspaceProjectAdded(Project workspaceProject);
|
public abstract void WorkspaceProjectAdded(Project workspaceProject);
|
||||||
|
|
||||||
public abstract void WorkspaceProjectChanged(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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
|
||||||
using Microsoft.CodeAnalysis.Razor;
|
using Microsoft.CodeAnalysis.Razor;
|
||||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||||
|
|
||||||
|
|
@ -27,42 +23,5 @@ namespace Microsoft.CodeAnalysis.Remote.Razor
|
||||||
|
|
||||||
return await RazorServices.TagHelperResolver.GetTagHelpersAsync(project, factoryTypeName, cancellationToken);
|
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;
|
FilePath = filePath;
|
||||||
Configuration = configuration;
|
Configuration = configuration;
|
||||||
HostProject = new HostProject(filePath, configuration);
|
|
||||||
WorkspaceProject = workspaceProject;
|
WorkspaceProject = workspaceProject;
|
||||||
TagHelpers = Array.Empty<TagHelperDescriptor>();
|
|
||||||
|
|
||||||
IsInitialized = true;
|
IsInitialized = true;
|
||||||
Version = VersionStamp.Default;
|
Version = VersionStamp.Default;
|
||||||
|
|
@ -58,6 +56,8 @@ namespace Microsoft.CodeAnalysis.Remote.Razor
|
||||||
|
|
||||||
public override RazorConfiguration Configuration { get; }
|
public override RazorConfiguration Configuration { get; }
|
||||||
|
|
||||||
|
public override IEnumerable<string> DocumentFilePaths => Array.Empty<string>();
|
||||||
|
|
||||||
public override string FilePath { get; }
|
public override string FilePath { get; }
|
||||||
|
|
||||||
public override bool IsInitialized { get; }
|
public override bool IsInitialized { get; }
|
||||||
|
|
@ -66,9 +66,30 @@ namespace Microsoft.CodeAnalysis.Remote.Razor
|
||||||
|
|
||||||
public override Project WorkspaceProject { get; }
|
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 FileChangeTrackerFactory _fileChangeTrackerFactory;
|
||||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||||
private readonly ErrorReporter _errorReporter;
|
private readonly ErrorReporter _errorReporter;
|
||||||
private readonly RazorProjectEngineFactoryService _projectEngineFactoryService;
|
|
||||||
private readonly Dictionary<string, ImportTracker> _importTrackerCache;
|
private readonly Dictionary<string, ImportTracker> _importTrackerCache;
|
||||||
|
|
||||||
public override event EventHandler<ImportChangedEventArgs> Changed;
|
public override event EventHandler<ImportChangedEventArgs> Changed;
|
||||||
|
|
@ -24,8 +23,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
public DefaultImportDocumentManager(
|
public DefaultImportDocumentManager(
|
||||||
ForegroundDispatcher foregroundDispatcher,
|
ForegroundDispatcher foregroundDispatcher,
|
||||||
ErrorReporter errorReporter,
|
ErrorReporter errorReporter,
|
||||||
FileChangeTrackerFactory fileChangeTrackerFactory,
|
FileChangeTrackerFactory fileChangeTrackerFactory)
|
||||||
RazorProjectEngineFactoryService projectEngineFactoryService)
|
|
||||||
{
|
{
|
||||||
if (foregroundDispatcher == null)
|
if (foregroundDispatcher == null)
|
||||||
{
|
{
|
||||||
|
|
@ -42,15 +40,9 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
throw new ArgumentNullException(nameof(fileChangeTrackerFactory));
|
throw new ArgumentNullException(nameof(fileChangeTrackerFactory));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (projectEngineFactoryService == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(projectEngineFactoryService));
|
|
||||||
}
|
|
||||||
|
|
||||||
_foregroundDispatcher = foregroundDispatcher;
|
_foregroundDispatcher = foregroundDispatcher;
|
||||||
_errorReporter = errorReporter;
|
_errorReporter = errorReporter;
|
||||||
_fileChangeTrackerFactory = fileChangeTrackerFactory;
|
_fileChangeTrackerFactory = fileChangeTrackerFactory;
|
||||||
_projectEngineFactoryService = projectEngineFactoryService;
|
|
||||||
_importTrackerCache = new Dictionary<string, ImportTracker>(StringComparer.OrdinalIgnoreCase);
|
_importTrackerCache = new Dictionary<string, ImportTracker>(StringComparer.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -115,8 +107,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
|
|
||||||
private IEnumerable<RazorProjectItem> GetImportItems(VisualStudioDocumentTracker tracker)
|
private IEnumerable<RazorProjectItem> GetImportItems(VisualStudioDocumentTracker tracker)
|
||||||
{
|
{
|
||||||
var projectDirectory = Path.GetDirectoryName(tracker.ProjectPath);
|
var projectEngine = tracker.ProjectSnapshot.GetProjectEngine();
|
||||||
var projectEngine = _projectEngineFactoryService.Create(projectDirectory, _ => { });
|
|
||||||
var trackerItem = projectEngine.FileSystem.GetItem(tracker.FilePath);
|
var trackerItem = projectEngine.FileSystem.GetItem(tracker.FilePath);
|
||||||
var importFeature = projectEngine.ProjectFeatures.OfType<IImportProjectFeature>().FirstOrDefault();
|
var importFeature = projectEngine.ProjectFeatures.OfType<IImportProjectFeature>().FirstOrDefault();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,13 +35,8 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
|
|
||||||
var errorReporter = languageServices.WorkspaceServices.GetRequiredService<ErrorReporter>();
|
var errorReporter = languageServices.WorkspaceServices.GetRequiredService<ErrorReporter>();
|
||||||
var fileChangeTrackerFactory = languageServices.GetRequiredService<FileChangeTrackerFactory>();
|
var fileChangeTrackerFactory = languageServices.GetRequiredService<FileChangeTrackerFactory>();
|
||||||
var projectEngineFactoryService = languageServices.GetRequiredService<RazorProjectEngineFactoryService>();
|
|
||||||
|
|
||||||
return new DefaultImportDocumentManager(
|
return new DefaultImportDocumentManager(_foregroundDispatcher, errorReporter, fileChangeTrackerFactory);
|
||||||
_foregroundDispatcher,
|
|
||||||
errorReporter,
|
|
||||||
fileChangeTrackerFactory,
|
|
||||||
projectEngineFactoryService);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
|
||||||
using Microsoft.CodeAnalysis.Razor;
|
using Microsoft.CodeAnalysis.Razor;
|
||||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||||
|
|
||||||
|
|
@ -12,18 +11,6 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
internal class DefaultTagHelperResolver : TagHelperResolver
|
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)
|
public override Task<TagHelperResolutionResult> GetTagHelpersAsync(ProjectSnapshot project, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (project == null)
|
if (project == null)
|
||||||
|
|
@ -35,9 +22,8 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
return Task.FromResult(TagHelperResolutionResult.Empty);
|
return Task.FromResult(TagHelperResolutionResult.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
var engine = _engineFactory.Create(project, RazorProjectFileSystem.Empty, b => { });
|
return GetTagHelpersAsync(project, project.GetProjectEngine());
|
||||||
return GetTagHelpersAsync(project, engine);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
||||||
{
|
{
|
||||||
return new DefaultTagHelperResolver(languageServices.GetRequiredService<RazorProjectEngineFactoryService>());
|
return new DefaultTagHelperResolver();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,6 +3,9 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.Razor;
|
using Microsoft.CodeAnalysis.Razor;
|
||||||
|
|
@ -25,7 +28,13 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
private readonly List<ITextView> _textViews;
|
private readonly List<ITextView> _textViews;
|
||||||
private readonly Workspace _workspace;
|
private readonly Workspace _workspace;
|
||||||
private bool _isSupportedProject;
|
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;
|
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.
|
_workspace = workspace; // For now we assume that the workspace is the always default VS workspace.
|
||||||
|
|
||||||
_textViews = new List<ITextView>();
|
_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 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 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;
|
public override ITextBuffer TextBuffer => _textBuffer;
|
||||||
|
|
||||||
|
|
@ -111,6 +126,8 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
|
|
||||||
public override string ProjectPath => _projectPath;
|
public override string ProjectPath => _projectPath;
|
||||||
|
|
||||||
|
public Task PendingTagHelperTask => _computingTagHelpers.task ?? Task.CompletedTask;
|
||||||
|
|
||||||
internal void AddTextView(ITextView textView)
|
internal void AddTextView(ITextView textView)
|
||||||
{
|
{
|
||||||
if (textView == null)
|
if (textView == null)
|
||||||
|
|
@ -118,6 +135,8 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
throw new ArgumentNullException(nameof(textView));
|
throw new ArgumentNullException(nameof(textView));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_foregroundDispatcher.AssertForegroundThread();
|
||||||
|
|
||||||
if (!_textViews.Contains(textView))
|
if (!_textViews.Contains(textView))
|
||||||
{
|
{
|
||||||
_textViews.Add(textView);
|
_textViews.Add(textView);
|
||||||
|
|
@ -131,6 +150,8 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
throw new ArgumentNullException(nameof(textView));
|
throw new ArgumentNullException(nameof(textView));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_foregroundDispatcher.AssertForegroundThread();
|
||||||
|
|
||||||
if (_textViews.Contains(textView))
|
if (_textViews.Contains(textView))
|
||||||
{
|
{
|
||||||
_textViews.Remove(textView);
|
_textViews.Remove(textView);
|
||||||
|
|
@ -139,6 +160,8 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
|
|
||||||
public override ITextView GetFocusedTextView()
|
public override ITextView GetFocusedTextView()
|
||||||
{
|
{
|
||||||
|
_foregroundDispatcher.AssertForegroundThread();
|
||||||
|
|
||||||
for (var i = 0; i < TextViews.Count; i++)
|
for (var i = 0; i < TextViews.Count; i++)
|
||||||
{
|
{
|
||||||
if (TextViews[i].HasAggregateFocus)
|
if (TextViews[i].HasAggregateFocus)
|
||||||
|
|
@ -152,20 +175,23 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
|
|
||||||
public void Subscribe()
|
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);
|
_importDocumentManager.OnSubscribed(this);
|
||||||
|
|
||||||
_workspaceEditorSettings.Changed += EditorSettingsManager_Changed;
|
OnContextChanged(ContextChangeKind.ProjectChanged);
|
||||||
_projectManager.Changed += ProjectManager_Changed;
|
|
||||||
_importDocumentManager.Changed += Import_Changed;
|
|
||||||
|
|
||||||
_isSupportedProject = true;
|
|
||||||
_project = _projectManager.GetProjectWithFilePath(_projectPath);
|
|
||||||
|
|
||||||
OnContextChanged(_project, ContextChangeKind.ProjectChanged);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Unsubscribe()
|
public void Unsubscribe()
|
||||||
{
|
{
|
||||||
|
_foregroundDispatcher.AssertForegroundThread();
|
||||||
|
|
||||||
_importDocumentManager.OnUnsubscribed(this);
|
_importDocumentManager.OnUnsubscribed(this);
|
||||||
|
|
||||||
_projectManager.Changed -= ProjectManager_Changed;
|
_projectManager.Changed -= ProjectManager_Changed;
|
||||||
|
|
@ -174,37 +200,112 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
|
|
||||||
// Detached from project.
|
// Detached from project.
|
||||||
_isSupportedProject = false;
|
_isSupportedProject = false;
|
||||||
_project = null;
|
_projectSnapshot = null;
|
||||||
|
OnContextChanged(kind: ContextChangeKind.ProjectChanged);
|
||||||
OnContextChanged(project: null, kind: ContextChangeKind.ProjectChanged);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnContextChanged(ProjectSnapshot project, ContextChangeKind kind)
|
private void StartComputingTagHelpers()
|
||||||
{
|
{
|
||||||
_foregroundDispatcher.AssertForegroundThread();
|
_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;
|
var handler = ContextChanged;
|
||||||
if (handler != null)
|
if (handler != null)
|
||||||
{
|
{
|
||||||
handler(this, new ContextChangeEventArgs(kind));
|
handler(this, new ContextChangeEventArgs(kind));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (kind == ContextChangeKind.ProjectChanged &&
|
||||||
|
_projectSnapshot != null &&
|
||||||
|
_computingTagHelpers.project == null)
|
||||||
|
{
|
||||||
|
StartComputingTagHelpers();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal for testing
|
// Internal for testing
|
||||||
internal void ProjectManager_Changed(object sender, ProjectChangeEventArgs e)
|
internal void ProjectManager_Changed(object sender, ProjectChangeEventArgs e)
|
||||||
{
|
{
|
||||||
|
_foregroundDispatcher.AssertForegroundThread();
|
||||||
|
|
||||||
if (_projectPath != null &&
|
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);
|
case ProjectChangeKind.DocumentsChanged:
|
||||||
}
|
|
||||||
else
|
// Nothing to do.
|
||||||
{
|
break;
|
||||||
OnContextChanged(e.Project, ContextChangeKind.ProjectChanged);
|
|
||||||
|
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 for testing
|
||||||
internal void EditorSettingsManager_Changed(object sender, EditorSettingsChangedEventArgs args)
|
internal void EditorSettingsManager_Changed(object sender, EditorSettingsChangedEventArgs args)
|
||||||
{
|
{
|
||||||
OnContextChanged(_project, ContextChangeKind.EditorSettingsChanged);
|
_foregroundDispatcher.AssertForegroundThread();
|
||||||
|
|
||||||
|
OnContextChanged(ContextChangeKind.EditorSettingsChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal for testing
|
// Internal for testing
|
||||||
internal void Import_Changed(object sender, ImportChangedEventArgs args)
|
internal void Import_Changed(object sender, ImportChangedEventArgs args)
|
||||||
{
|
{
|
||||||
|
_foregroundDispatcher.AssertForegroundThread();
|
||||||
|
|
||||||
foreach (var path in args.AssociatedDocuments)
|
foreach (var path in args.AssociatedDocuments)
|
||||||
{
|
{
|
||||||
if (string.Equals(_filePath, path, StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(_filePath, path, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
OnContextChanged(_project, ContextChangeKind.ImportsChanged);
|
OnContextChanged(ContextChangeKind.ImportsChanged);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -31,7 +32,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
private readonly VisualStudioCompletionBroker _completionBroker;
|
private readonly VisualStudioCompletionBroker _completionBroker;
|
||||||
private readonly VisualStudioDocumentTracker _documentTracker;
|
private readonly VisualStudioDocumentTracker _documentTracker;
|
||||||
private readonly ForegroundDispatcher _dispatcher;
|
private readonly ForegroundDispatcher _dispatcher;
|
||||||
private readonly RazorProjectEngineFactoryService _projectEngineFactory;
|
private readonly ProjectSnapshotProjectEngineFactory _projectEngineFactory;
|
||||||
private readonly ErrorReporter _errorReporter;
|
private readonly ErrorReporter _errorReporter;
|
||||||
private RazorProjectEngine _projectEngine;
|
private RazorProjectEngine _projectEngine;
|
||||||
private RazorCodeDocument _codeDocument;
|
private RazorCodeDocument _codeDocument;
|
||||||
|
|
@ -47,7 +48,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
public DefaultVisualStudioRazorParser(
|
public DefaultVisualStudioRazorParser(
|
||||||
ForegroundDispatcher dispatcher,
|
ForegroundDispatcher dispatcher,
|
||||||
VisualStudioDocumentTracker documentTracker,
|
VisualStudioDocumentTracker documentTracker,
|
||||||
RazorProjectEngineFactoryService projectEngineFactory,
|
ProjectSnapshotProjectEngineFactory projectEngineFactory,
|
||||||
ErrorReporter errorReporter,
|
ErrorReporter errorReporter,
|
||||||
VisualStudioCompletionBroker completionBroker)
|
VisualStudioCompletionBroker completionBroker)
|
||||||
{
|
{
|
||||||
|
|
@ -167,8 +168,18 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
_dispatcher.AssertForegroundThread();
|
_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);
|
var projectDirectory = Path.GetDirectoryName(_documentTracker.ProjectPath);
|
||||||
_projectEngine = _projectEngineFactory.Create(projectDirectory, ConfigureProjectEngine);
|
|
||||||
_parser = new BackgroundParser(_projectEngine, FilePath, projectDirectory);
|
_parser = new BackgroundParser(_projectEngine, FilePath, projectDirectory);
|
||||||
_parser.ResultsReady += OnResultsReady;
|
_parser.ResultsReady += OnResultsReady;
|
||||||
_parser.Start();
|
_parser.Start();
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
internal class DefaultVisualStudioRazorParserFactory : VisualStudioRazorParserFactory
|
internal class DefaultVisualStudioRazorParserFactory : VisualStudioRazorParserFactory
|
||||||
{
|
{
|
||||||
private readonly ForegroundDispatcher _dispatcher;
|
private readonly ForegroundDispatcher _dispatcher;
|
||||||
private readonly RazorProjectEngineFactoryService _projectEngineFactoryService;
|
private readonly ProjectSnapshotProjectEngineFactory _projectEngineFactory;
|
||||||
private readonly VisualStudioCompletionBroker _completionBroker;
|
private readonly VisualStudioCompletionBroker _completionBroker;
|
||||||
private readonly ErrorReporter _errorReporter;
|
private readonly ErrorReporter _errorReporter;
|
||||||
|
|
||||||
|
|
@ -17,7 +17,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
ForegroundDispatcher dispatcher,
|
ForegroundDispatcher dispatcher,
|
||||||
ErrorReporter errorReporter,
|
ErrorReporter errorReporter,
|
||||||
VisualStudioCompletionBroker completionBroker,
|
VisualStudioCompletionBroker completionBroker,
|
||||||
RazorProjectEngineFactoryService projectEngineFactoryService)
|
ProjectSnapshotProjectEngineFactory projectEngineFactory)
|
||||||
{
|
{
|
||||||
if (dispatcher == null)
|
if (dispatcher == null)
|
||||||
{
|
{
|
||||||
|
|
@ -34,15 +34,15 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
throw new ArgumentNullException(nameof(completionBroker));
|
throw new ArgumentNullException(nameof(completionBroker));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (projectEngineFactoryService == null)
|
if (projectEngineFactory == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(projectEngineFactoryService));
|
throw new ArgumentNullException(nameof(projectEngineFactory));
|
||||||
}
|
}
|
||||||
|
|
||||||
_dispatcher = dispatcher;
|
_dispatcher = dispatcher;
|
||||||
_errorReporter = errorReporter;
|
_errorReporter = errorReporter;
|
||||||
_completionBroker = completionBroker;
|
_completionBroker = completionBroker;
|
||||||
_projectEngineFactoryService = projectEngineFactoryService;
|
_projectEngineFactory = projectEngineFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override VisualStudioRazorParser Create(VisualStudioDocumentTracker documentTracker)
|
public override VisualStudioRazorParser Create(VisualStudioDocumentTracker documentTracker)
|
||||||
|
|
@ -57,7 +57,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
var parser = new DefaultVisualStudioRazorParser(
|
var parser = new DefaultVisualStudioRazorParser(
|
||||||
_dispatcher,
|
_dispatcher,
|
||||||
documentTracker,
|
documentTracker,
|
||||||
_projectEngineFactoryService,
|
_projectEngineFactory,
|
||||||
_errorReporter,
|
_errorReporter,
|
||||||
_completionBroker);
|
_completionBroker);
|
||||||
return parser;
|
return parser;
|
||||||
|
|
|
||||||
|
|
@ -35,13 +35,13 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
var workspaceServices = languageServices.WorkspaceServices;
|
var workspaceServices = languageServices.WorkspaceServices;
|
||||||
var errorReporter = workspaceServices.GetRequiredService<ErrorReporter>();
|
var errorReporter = workspaceServices.GetRequiredService<ErrorReporter>();
|
||||||
var completionBroker = languageServices.GetRequiredService<VisualStudioCompletionBroker>();
|
var completionBroker = languageServices.GetRequiredService<VisualStudioCompletionBroker>();
|
||||||
var projectEngineFactoryService = languageServices.GetRequiredService<RazorProjectEngineFactoryService>();
|
var projectEngineFactory = workspaceServices.GetRequiredService<ProjectSnapshotProjectEngineFactory>();
|
||||||
|
|
||||||
return new DefaultVisualStudioRazorParserFactory(
|
return new DefaultVisualStudioRazorParserFactory(
|
||||||
_foregroundDispatcher,
|
_foregroundDispatcher,
|
||||||
errorReporter,
|
errorReporter,
|
||||||
completionBroker,
|
completionBroker,
|
||||||
projectEngineFactoryService);
|
projectEngineFactory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.Razor.Editor;
|
using Microsoft.CodeAnalysis.Razor.Editor;
|
||||||
|
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||||
using Microsoft.VisualStudio.Text;
|
using Microsoft.VisualStudio.Text;
|
||||||
using Microsoft.VisualStudio.Text.Editor;
|
using Microsoft.VisualStudio.Text.Editor;
|
||||||
|
|
||||||
|
|
@ -29,6 +30,8 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
|
|
||||||
public abstract Project Project { get; }
|
public abstract Project Project { get; }
|
||||||
|
|
||||||
|
internal abstract ProjectSnapshot ProjectSnapshot { get; }
|
||||||
|
|
||||||
public abstract Workspace Workspace { get; }
|
public abstract Workspace Workspace { get; }
|
||||||
|
|
||||||
public abstract ITextBuffer TextBuffer { 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"/>
|
<Reference Include="System.Xaml"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
The ProjectSystem.SDK tasks that handle XamlPropertyRule don't work on the dotnet core version
|
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
|
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.
|
checked in. Then we don't need to generate it at build time.
|
||||||
|
|
@ -59,6 +59,9 @@
|
||||||
<None Include="$(RulesDirectory)RazorGeneral.xaml">
|
<None Include="$(RulesDirectory)RazorGeneral.xaml">
|
||||||
<Link>ProjectSystem\Rules\RazorGeneral.xaml</Link>
|
<Link>ProjectSystem\Rules\RazorGeneral.xaml</Link>
|
||||||
</None>
|
</None>
|
||||||
|
<None Include="$(RulesDirectory)RazorGenerateWithTargetPath.xaml">
|
||||||
|
<Link>ProjectSystem\Rules\RazorGenerateWithTargetPath.xaml</Link>
|
||||||
|
</None>
|
||||||
<EmbeddedResource Include="$(RulesDirectory)RazorConfiguration.xaml">
|
<EmbeddedResource Include="$(RulesDirectory)RazorConfiguration.xaml">
|
||||||
<LogicalName>XamlRuleToCode:RazorConfiguration.xaml</LogicalName>
|
<LogicalName>XamlRuleToCode:RazorConfiguration.xaml</LogicalName>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
|
|
@ -68,6 +71,9 @@
|
||||||
<EmbeddedResource Include="$(RulesDirectory)RazorGeneral.xaml">
|
<EmbeddedResource Include="$(RulesDirectory)RazorGeneral.xaml">
|
||||||
<LogicalName>XamlRuleToCode:RazorGeneral.xaml</LogicalName>
|
<LogicalName>XamlRuleToCode:RazorGeneral.xaml</LogicalName>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="$(RulesDirectory)RazorGenerateWithTargetPath.xaml">
|
||||||
|
<LogicalName>XamlRuleToCode:RazorGenerateWithTargetPath.xaml</LogicalName>
|
||||||
|
</EmbeddedResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Condition="'$(MSBuildRuntimeType)'!='Core'">
|
<ItemGroup Condition="'$(MSBuildRuntimeType)'!='Core'">
|
||||||
<XamlPropertyRule Include="$(RulesDirectory)RazorConfiguration.xaml">
|
<XamlPropertyRule Include="$(RulesDirectory)RazorConfiguration.xaml">
|
||||||
|
|
@ -92,6 +98,13 @@
|
||||||
<RuleInjectionClassName>RazorProjectProperties</RuleInjectionClassName>
|
<RuleInjectionClassName>RazorProjectProperties</RuleInjectionClassName>
|
||||||
<OutputPath>ProjectSystem\Rules\</OutputPath>
|
<OutputPath>ProjectSystem\Rules\</OutputPath>
|
||||||
</XamlPropertyRule>
|
</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>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Update="ProjectSystem\Rules\RazorConfiguration.cs">
|
<Compile Update="ProjectSystem\Rules\RazorConfiguration.cs">
|
||||||
|
|
@ -103,6 +116,9 @@
|
||||||
<Compile Update="ProjectSystem\Rules\RazorGeneral.cs">
|
<Compile Update="ProjectSystem\Rules\RazorGeneral.cs">
|
||||||
<DependentUpon>ProjectSystem\Rules\RazorGeneral.xaml</DependentUpon>
|
<DependentUpon>ProjectSystem\Rules\RazorGeneral.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Update="ProjectSystem\Rules\RazorGenerateWithTargetPath.cs">
|
||||||
|
<DependentUpon>ProjectSystem\Rules\RazorGenerateWithTargetPath.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
|
|
||||||
|
|
@ -16,15 +16,15 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
internal class OOPTagHelperResolver : TagHelperResolver
|
internal class OOPTagHelperResolver : TagHelperResolver
|
||||||
{
|
{
|
||||||
private readonly DefaultTagHelperResolver _defaultResolver;
|
private readonly DefaultTagHelperResolver _defaultResolver;
|
||||||
private readonly RazorProjectEngineFactoryService _engineFactory;
|
private readonly ProjectSnapshotProjectEngineFactory _factory;
|
||||||
private readonly ErrorReporter _errorReporter;
|
private readonly ErrorReporter _errorReporter;
|
||||||
private readonly Workspace _workspace;
|
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)
|
if (errorReporter == null)
|
||||||
|
|
@ -37,11 +37,11 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
throw new ArgumentNullException(nameof(workspace));
|
throw new ArgumentNullException(nameof(workspace));
|
||||||
}
|
}
|
||||||
|
|
||||||
_engineFactory = engineFactory;
|
_factory = factory;
|
||||||
_errorReporter = errorReporter;
|
_errorReporter = errorReporter;
|
||||||
_workspace = workspace;
|
_workspace = workspace;
|
||||||
|
|
||||||
_defaultResolver = new DefaultTagHelperResolver(_engineFactory);
|
_defaultResolver = new DefaultTagHelperResolver();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<TagHelperResolutionResult> GetTagHelpersAsync(ProjectSnapshot project, CancellationToken cancellationToken = default)
|
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
|
// 3. Use fallback factory in process
|
||||||
//
|
//
|
||||||
// Calling into RazorTemplateEngineFactoryService.Create will accomplish #2 and #3 in one step.
|
// Calling into RazorTemplateEngineFactoryService.Create will accomplish #2 and #3 in one step.
|
||||||
var factory = _engineFactory.FindSerializableFactory(project);
|
var factory = _factory.FindSerializableFactory(project);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,19 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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;
|
||||||
using Microsoft.CodeAnalysis.Host.Mef;
|
using Microsoft.CodeAnalysis.Host.Mef;
|
||||||
using Microsoft.CodeAnalysis.Razor;
|
using Microsoft.CodeAnalysis.Razor;
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
{
|
{
|
||||||
[Shared]
|
|
||||||
[ExportLanguageServiceFactory(typeof(TagHelperResolver), RazorLanguage.Name, ServiceLayer.Host)]
|
[ExportLanguageServiceFactory(typeof(TagHelperResolver), RazorLanguage.Name, ServiceLayer.Host)]
|
||||||
internal class OOPTagHelperResolverFactory : ILanguageServiceFactory
|
internal class OOPTagHelperResolverFactory : ILanguageServiceFactory
|
||||||
{
|
{
|
||||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
||||||
{
|
{
|
||||||
return new OOPTagHelperResolver(
|
return new OOPTagHelperResolver(
|
||||||
languageServices.GetRequiredService<RazorProjectEngineFactoryService>(),
|
languageServices.WorkspaceServices.GetRequiredService<ProjectSnapshotProjectEngineFactory>(),
|
||||||
languageServices.WorkspaceServices.GetRequiredService<ErrorReporter>(),
|
languageServices.WorkspaceServices.GetRequiredService<ErrorReporter>(),
|
||||||
languageServices.WorkspaceServices.Workspace);
|
languageServices.WorkspaceServices.Workspace);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.ComponentModel.Composition;
|
using System.ComponentModel.Composition;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -13,8 +14,7 @@ using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Microsoft.VisualStudio.LanguageServices;
|
using Microsoft.VisualStudio.LanguageServices;
|
||||||
using Microsoft.VisualStudio.ProjectSystem;
|
using Microsoft.VisualStudio.ProjectSystem;
|
||||||
using Microsoft.VisualStudio.ProjectSystem.Properties;
|
using Microsoft.VisualStudio.ProjectSystem.Properties;
|
||||||
using ProjectState = System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IProjectRuleSnapshot>;
|
using Item = System.Collections.Generic.KeyValuePair<string, System.Collections.Immutable.IImmutableDictionary<string, string>>;
|
||||||
using ProjectStateItem = System.Collections.Generic.KeyValuePair<string, System.Collections.Immutable.IImmutableDictionary<string, string>>;
|
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
{
|
{
|
||||||
|
|
@ -62,7 +62,13 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
receiver,
|
receiver,
|
||||||
initialDataAsNew: true,
|
initialDataAsNew: true,
|
||||||
suppressVersionOnlyUpdates: 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)
|
protected override async Task DisposeCoreAsync(bool initialized)
|
||||||
|
|
@ -90,12 +96,36 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
if (TryGetConfiguration(update.Value.CurrentState, out var configuration))
|
if (TryGetConfiguration(update.Value.CurrentState, out var configuration))
|
||||||
{
|
{
|
||||||
var hostProject = new HostProject(CommonServices.UnconfiguredProject.FullPath, 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
|
else
|
||||||
{
|
{
|
||||||
// Ok we can't find a configuration. Let's assume this project isn't using Razor then.
|
// 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);
|
}, registerFaultHandler: true);
|
||||||
|
|
@ -103,34 +133,34 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
||||||
// Internal for testing
|
// Internal for testing
|
||||||
internal static bool TryGetConfiguration(
|
internal static bool TryGetConfiguration(
|
||||||
ProjectState projectState,
|
IImmutableDictionary<string, IProjectRuleSnapshot> state,
|
||||||
out RazorConfiguration configuration)
|
out RazorConfiguration configuration)
|
||||||
{
|
{
|
||||||
if (!TryGetDefaultConfiguration(projectState, out var defaultConfiguration))
|
if (!TryGetDefaultConfiguration(state, out var defaultConfiguration))
|
||||||
{
|
{
|
||||||
configuration = null;
|
configuration = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TryGetLanguageVersion(projectState, out var languageVersion))
|
if (!TryGetLanguageVersion(state, out var languageVersion))
|
||||||
{
|
{
|
||||||
configuration = null;
|
configuration = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TryGetConfigurationItem(defaultConfiguration, projectState, out var configurationItem))
|
if (!TryGetConfigurationItem(defaultConfiguration, state, out var configurationItem))
|
||||||
{
|
{
|
||||||
configuration = null;
|
configuration = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TryGetConfiguredExtensionNames(configurationItem, out var configuredExtensionNames))
|
if (!TryGetExtensionNames(configurationItem, out var extensionNames))
|
||||||
{
|
{
|
||||||
configuration = null;
|
configuration = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TryGetExtensions(configuredExtensionNames, projectState, out var extensions))
|
if (!TryGetExtensions(extensionNames, state, out var extensions))
|
||||||
{
|
{
|
||||||
configuration = null;
|
configuration = null;
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -142,9 +172,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
||||||
|
|
||||||
// Internal for testing
|
// 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;
|
defaultConfiguration = null;
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -166,9 +198,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal for testing
|
// 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;
|
languageVersion = null;
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -197,17 +231,17 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
// Internal for testing
|
// Internal for testing
|
||||||
internal static bool TryGetConfigurationItem(
|
internal static bool TryGetConfigurationItem(
|
||||||
string configuration,
|
string configuration,
|
||||||
ProjectState projectState,
|
IImmutableDictionary<string, IProjectRuleSnapshot> state,
|
||||||
out ProjectStateItem configurationItem)
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var razorConfigurationItems = configurationState.Items;
|
var items = configurationState.Items;
|
||||||
foreach (var item in razorConfigurationItems)
|
foreach (var item in items)
|
||||||
{
|
{
|
||||||
if (item.Key == configuration)
|
if (item.Key == configuration)
|
||||||
{
|
{
|
||||||
|
|
@ -216,44 +250,49 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
configurationItem = default(ProjectStateItem);
|
configurationItem = default(Item);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal for testing
|
// 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;
|
configuredExtensionNames = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(extensionNamesValue))
|
if (string.IsNullOrEmpty(extensionNames))
|
||||||
{
|
{
|
||||||
configuredExtensionNames = null;
|
configuredExtensionNames = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
configuredExtensionNames = extensionNamesValue.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
configuredExtensionNames = extensionNames.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal for testing
|
// 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;
|
extensions = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var extensionItems = extensionState.Items;
|
var items = rule.Items;
|
||||||
var extensionList = new List<ProjectSystemRazorExtension>();
|
var extensionList = new List<ProjectSystemRazorExtension>();
|
||||||
foreach (var item in extensionItems)
|
foreach (var item in items)
|
||||||
{
|
{
|
||||||
var extensionName = item.Key;
|
var extensionName = item.Key;
|
||||||
if (configuredExtensionNames.Contains(extensionName))
|
if (extensionNames.Contains(extensionName))
|
||||||
{
|
{
|
||||||
extensionList.Add(new ProjectSystemRazorExtension(extensionName));
|
extensionList.Add(new ProjectSystemRazorExtension(extensionName));
|
||||||
}
|
}
|
||||||
|
|
@ -262,5 +301,53 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
extensions = extensionList.ToArray();
|
extensions = extensionList.ToArray();
|
||||||
return true;
|
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 () =>
|
await ExecuteWithLock(async () =>
|
||||||
{
|
{
|
||||||
string mvcReferenceFullPath = null;
|
string mvcReferenceFullPath = null;
|
||||||
var references = update.Value.CurrentState[ResolvedCompilationReference.SchemaName].Items;
|
if (update.Value.CurrentState.ContainsKey(ResolvedCompilationReference.SchemaName))
|
||||||
foreach (var reference in references)
|
|
||||||
{
|
{
|
||||||
if (reference.Key.EndsWith(MvcAssemblyFileName, StringComparison.OrdinalIgnoreCase))
|
var references = update.Value.CurrentState[ResolvedCompilationReference.SchemaName].Items;
|
||||||
|
foreach (var reference in references)
|
||||||
{
|
{
|
||||||
mvcReferenceFullPath = reference.Key;
|
if (reference.Key.EndsWith(MvcAssemblyFileName, StringComparison.OrdinalIgnoreCase))
|
||||||
break;
|
{
|
||||||
|
mvcReferenceFullPath = reference.Key;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mvcReferenceFullPath == null)
|
if (mvcReferenceFullPath == null)
|
||||||
{
|
{
|
||||||
// Ok we can't find an MVC version. Let's assume this project isn't using Razor then.
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,13 +109,16 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
if (version == null)
|
if (version == null)
|
||||||
{
|
{
|
||||||
// Ok we can't find an MVC version. Let's assume this project isn't using Razor then.
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var configuration = FallbackRazorConfiguration.SelectConfiguration(version);
|
var configuration = FallbackRazorConfiguration.SelectConfiguration(version);
|
||||||
var hostProject = new HostProject(CommonServices.UnconfiguredProject.FullPath, configuration);
|
var hostProject = new HostProject(CommonServices.UnconfiguredProject.FullPath, configuration);
|
||||||
await UpdateProjectUnsafeAsync(hostProject).ConfigureAwait(false);
|
await UpdateAsync(() =>
|
||||||
|
{
|
||||||
|
UpdateProjectUnsafe(hostProject);
|
||||||
|
}).ConfigureAwait(false);
|
||||||
});
|
});
|
||||||
}, registerFaultHandler: true);
|
}, registerFaultHandler: true);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,10 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.Composition;
|
using System.ComponentModel.Composition;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.LanguageServices;
|
using Microsoft.VisualStudio.LanguageServices;
|
||||||
|
|
@ -18,6 +21,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
||||||
private ProjectSnapshotManagerBase _projectManager;
|
private ProjectSnapshotManagerBase _projectManager;
|
||||||
private HostProject _current;
|
private HostProject _current;
|
||||||
|
private Dictionary<string, HostDocument> _currentDocuments;
|
||||||
|
|
||||||
public RazorProjectHostBase(
|
public RazorProjectHostBase(
|
||||||
IUnconfiguredProjectCommonServices commonServices,
|
IUnconfiguredProjectCommonServices commonServices,
|
||||||
|
|
@ -38,6 +42,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
_workspace = workspace;
|
_workspace = workspace;
|
||||||
|
|
||||||
_lock = new AsyncSemaphore(initialCount: 1);
|
_lock = new AsyncSemaphore(initialCount: 1);
|
||||||
|
_currentDocuments = new Dictionary<string, HostDocument>(FilePathComparer.Instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal for testing
|
// Internal for testing
|
||||||
|
|
@ -67,8 +72,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
_projectManager = projectManager;
|
_projectManager = projectManager;
|
||||||
|
|
||||||
_lock = new AsyncSemaphore(initialCount: 1);
|
_lock = new AsyncSemaphore(initialCount: 1);
|
||||||
|
_currentDocuments = new Dictionary<string, HostDocument>(FilePathComparer.Instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected HostProject Current => _current;
|
||||||
|
|
||||||
protected IUnconfiguredProjectCommonServices CommonServices { get; }
|
protected IUnconfiguredProjectCommonServices CommonServices { get; }
|
||||||
|
|
||||||
// internal for tests. The product will call through the IProjectDynamicLoadComponent interface.
|
// internal for tests. The product will call through the IProjectDynamicLoadComponent interface.
|
||||||
|
|
@ -94,7 +102,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
{
|
{
|
||||||
if (_current != null)
|
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)
|
if (_current != null)
|
||||||
{
|
{
|
||||||
var old = _current;
|
var old = _current;
|
||||||
await UpdateProjectUnsafeAsync(null).ConfigureAwait(false);
|
var oldDocuments = _currentDocuments.Values.ToArray();
|
||||||
|
|
||||||
var filePath = CommonServices.UnconfiguredProject.FullPath;
|
await UpdateAsync(UninitializeProjectUnsafe).ConfigureAwait(false);
|
||||||
await UpdateProjectUnsafeAsync(new HostProject(filePath, old.Configuration)).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;
|
return _projectManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must be called inside the lock.
|
protected async Task UpdateAsync(Action action)
|
||||||
protected async Task UpdateProjectUnsafeAsync(HostProject project)
|
|
||||||
{
|
{
|
||||||
await CommonServices.ThreadingService.SwitchToUIThread();
|
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)
|
if (_current == null && project == null)
|
||||||
{
|
{
|
||||||
// This is a no-op. This project isn't using Razor.
|
// 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)
|
else if (_current != null && project == null)
|
||||||
{
|
{
|
||||||
|
Debug.Assert(_currentDocuments.Count == 0);
|
||||||
projectManager.HostProjectRemoved(_current);
|
projectManager.HostProjectRemoved(_current);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -159,6 +188,40 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
||||||
_current = project;
|
_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)
|
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)
|
public int UpdateProjectCfg_Done(IVsHierarchy pHierProj, IVsCfg pCfgProj, IVsCfg pCfgSln, uint dwAction, int fSuccess, int fCancel)
|
||||||
{
|
{
|
||||||
var projectPath = _projectService.GetProjectPath(pHierProj);
|
var projectPath = _projectService.GetProjectPath(pHierProj);
|
||||||
|
var project = _projectManager.GetLoadedProject(projectPath);
|
||||||
// Get the corresponding roslyn project by matching the project name and the project path.
|
if (project != null && project.WorkspaceProject != null)
|
||||||
foreach (var projectSnapshot in _projectManager.Projects)
|
|
||||||
{
|
{
|
||||||
if (string.Equals(projectPath, projectSnapshot.FilePath, StringComparison.OrdinalIgnoreCase))
|
var workspaceProject = _projectManager.Workspace.CurrentSolution.GetProject(project.WorkspaceProject.Id);
|
||||||
|
if (workspaceProject != null)
|
||||||
{
|
{
|
||||||
_projectManager.HostProjectBuildComplete(projectSnapshot.HostProject);
|
// Trigger a tag helper update by forcing the project manager to see the workspace Project
|
||||||
break;
|
// from the current solution.
|
||||||
|
_projectManager.WorkspaceProjectChanged(workspaceProject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -97,14 +97,15 @@ namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor
|
||||||
}
|
}
|
||||||
|
|
||||||
var projectPath = _projectService.GetProjectPath(projectItem);
|
var projectPath = _projectService.GetProjectPath(projectItem);
|
||||||
|
var project = _projectManager.GetLoadedProject(projectPath);
|
||||||
// Get the corresponding roslyn project by matching the project name and the project path.
|
if (project != null && project.WorkspaceProject != null)
|
||||||
foreach (var projectSnapshot in _projectManager.Projects)
|
|
||||||
{
|
{
|
||||||
if (string.Equals(projectPath, projectSnapshot.FilePath, StringComparison.OrdinalIgnoreCase))
|
var workspaceProject = _projectManager.Workspace.CurrentSolution.GetProject(project.WorkspaceProject.Id);
|
||||||
|
if (workspaceProject != null)
|
||||||
{
|
{
|
||||||
_projectManager.HostProjectBuildComplete(projectSnapshot.HostProject);
|
// Trigger a tag helper update by forcing the project manager to see the workspace Project
|
||||||
break;
|
// from the current solution.
|
||||||
|
_projectManager.WorkspaceProjectChanged(workspaceProject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,12 @@ using Microsoft.CodeAnalysis.Razor;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Host
|
namespace Microsoft.CodeAnalysis.Host
|
||||||
{
|
{
|
||||||
internal class TestRazorLanguageServices : HostLanguageServices
|
internal class TestLanguageServices : HostLanguageServices
|
||||||
{
|
{
|
||||||
private readonly HostWorkspaceServices _workspaceServices;
|
private readonly HostWorkspaceServices _workspaceServices;
|
||||||
private readonly IEnumerable<ILanguageService> _languageServices;
|
private readonly IEnumerable<ILanguageService> _languageServices;
|
||||||
|
|
||||||
public TestRazorLanguageServices(HostWorkspaceServices workspaceServices, IEnumerable<ILanguageService> languageServices)
|
public TestLanguageServices(HostWorkspaceServices workspaceServices, IEnumerable<ILanguageService> languageServices)
|
||||||
{
|
{
|
||||||
if (workspaceServices == null)
|
if (workspaceServices == null)
|
||||||
{
|
{
|
||||||
|
|
@ -13,14 +13,14 @@ namespace Microsoft.CodeAnalysis.Host
|
||||||
private static readonly Workspace DefaultWorkspace = TestWorkspace.Create();
|
private static readonly Workspace DefaultWorkspace = TestWorkspace.Create();
|
||||||
|
|
||||||
private readonly HostServices _hostServices;
|
private readonly HostServices _hostServices;
|
||||||
|
private readonly HostLanguageServices _razorLanguageServices;
|
||||||
private readonly IEnumerable<IWorkspaceService> _workspaceServices;
|
private readonly IEnumerable<IWorkspaceService> _workspaceServices;
|
||||||
private readonly TestRazorLanguageServices _razorLanguageServices;
|
|
||||||
private readonly Workspace _workspace;
|
private readonly Workspace _workspace;
|
||||||
|
|
||||||
public TestWorkspaceServices(
|
public TestWorkspaceServices(
|
||||||
HostServices hostServices,
|
HostServices hostServices,
|
||||||
IEnumerable<IWorkspaceService> workspaceServices,
|
IEnumerable<IWorkspaceService> workspaceServices,
|
||||||
IEnumerable<ILanguageService> razorLanguageServices,
|
IEnumerable<ILanguageService> languageServices,
|
||||||
Workspace workspace)
|
Workspace workspace)
|
||||||
{
|
{
|
||||||
if (hostServices == null)
|
if (hostServices == null)
|
||||||
|
|
@ -33,9 +33,9 @@ namespace Microsoft.CodeAnalysis.Host
|
||||||
throw new ArgumentNullException(nameof(workspaceServices));
|
throw new ArgumentNullException(nameof(workspaceServices));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (razorLanguageServices == null)
|
if (languageServices == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(razorLanguageServices));
|
throw new ArgumentNullException(nameof(languageServices));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (workspace == null)
|
if (workspace == null)
|
||||||
|
|
@ -45,8 +45,9 @@ namespace Microsoft.CodeAnalysis.Host
|
||||||
|
|
||||||
_hostServices = hostServices;
|
_hostServices = hostServices;
|
||||||
_workspaceServices = workspaceServices;
|
_workspaceServices = workspaceServices;
|
||||||
_razorLanguageServices = new TestRazorLanguageServices(this, razorLanguageServices);
|
|
||||||
_workspace = workspace;
|
_workspace = workspace;
|
||||||
|
|
||||||
|
_razorLanguageServices = new TestLanguageServices(this, languageServices);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override HostServices HostServices => _hostServices;
|
public override HostServices HostServices => _hostServices;
|
||||||
|
|
@ -68,12 +69,13 @@ namespace Microsoft.CodeAnalysis.Host
|
||||||
|
|
||||||
public override HostLanguageServices GetLanguageServices(string languageName)
|
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 };
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
|
using Microsoft.CodeAnalysis.Host;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
{
|
{
|
||||||
public class DefaultProjectSnapshotTest
|
public class DefaultProjectSnapshotTest
|
||||||
{
|
{
|
||||||
[Fact]
|
public DefaultProjectSnapshotTest()
|
||||||
public void WithWorkspaceProject_CreatesSnapshot_UpdatesUnderlyingProject()
|
|
||||||
{
|
{
|
||||||
// Arrange
|
TagHelperResolver = new TestTagHelperResolver();
|
||||||
var hostProject = new HostProject("Test.cshtml", FallbackRazorConfiguration.MVC_2_0);
|
|
||||||
var workspaceProject = GetWorkspaceProject("Test1");
|
|
||||||
var original = new DefaultProjectSnapshot(hostProject, workspaceProject);
|
|
||||||
|
|
||||||
var anotherProject = GetWorkspaceProject("Test1");
|
HostServices = TestServices.Create(
|
||||||
|
new IWorkspaceService[]
|
||||||
// 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[]
|
|
||||||
{
|
{
|
||||||
TagHelperDescriptorBuilder.Create("One", "TestAssembly").Build(),
|
new TestProjectSnapshotProjectEngineFactory(),
|
||||||
TagHelperDescriptorBuilder.Create("Two", "TestAssembly").Build(),
|
|
||||||
},
|
},
|
||||||
|
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;
|
// Arrange
|
||||||
TestWorkspace.Create(workspace =>
|
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);
|
var state = new ProjectState(Workspace.Services, HostProject, WorkspaceProject);
|
||||||
});
|
var snapshot = new DefaultProjectSnapshot(state);
|
||||||
return project;
|
|
||||||
|
// 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.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -214,23 +212,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
private class TestProjectSnapshotManager : DefaultProjectSnapshotManager
|
private class TestProjectSnapshotManager : DefaultProjectSnapshotManager
|
||||||
{
|
{
|
||||||
public TestProjectSnapshotManager(IEnumerable<ProjectSnapshotChangeTrigger> triggers, Workspace workspace)
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Microsoft.CodeAnalysis.Razor;
|
using Microsoft.CodeAnalysis.Razor;
|
||||||
|
|
@ -13,23 +14,51 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
public class DefaultImportDocumentManagerIntegrationTest : ForegroundDispatcherTestBase
|
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]
|
[ForegroundFact]
|
||||||
public void Changed_TrackerChanged_ResultsInChangedHavingCorrectArgs()
|
public void Changed_TrackerChanged_ResultsInChangedHavingCorrectArgs()
|
||||||
{
|
{
|
||||||
// Arrange
|
// 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 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 tracker = Mock.Of<VisualStudioDocumentTracker>(
|
||||||
var projectEngineFactoryService = GetProjectEngineFactoryService();
|
t => t.FilePath == "C:\\path\\to\\project\\Views\\Home\\file.cshtml" &&
|
||||||
var fileChangeTracker = new Mock<FileChangeTracker>();
|
t.ProjectPath == ProjectPath &&
|
||||||
fileChangeTracker.Setup(f => f.FilePath).Returns(testImportsPath);
|
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 fileChangeTrackerFactory = new Mock<FileChangeTrackerFactory>();
|
||||||
|
var fileChangeTracker = new Mock<FileChangeTracker>();
|
||||||
|
fileChangeTracker
|
||||||
|
.Setup(f => f.FilePath)
|
||||||
|
.Returns(testImportsPath);
|
||||||
fileChangeTrackerFactory
|
fileChangeTrackerFactory
|
||||||
.Setup(f => f.Create(testImportsPath))
|
.Setup(f => f.Create(testImportsPath))
|
||||||
.Returns(fileChangeTracker.Object);
|
.Returns(fileChangeTracker.Object);
|
||||||
|
|
||||||
fileChangeTrackerFactory
|
fileChangeTrackerFactory
|
||||||
.Setup(f => f.Create("C:\\path\\to\\project\\Views\\_ViewImports.cshtml"))
|
.Setup(f => f.Create("C:\\path\\to\\project\\Views\\_ViewImports.cshtml"))
|
||||||
.Returns(Mock.Of<FileChangeTracker>());
|
.Returns(Mock.Of<FileChangeTracker>());
|
||||||
|
|
@ -38,7 +67,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
.Returns(Mock.Of<FileChangeTracker>());
|
.Returns(Mock.Of<FileChangeTracker>());
|
||||||
|
|
||||||
var called = false;
|
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(tracker);
|
||||||
manager.OnSubscribed(anotherTracker);
|
manager.OnSubscribed(anotherTracker);
|
||||||
manager.Changed += (sender, args) =>
|
manager.Changed += (sender, args) =>
|
||||||
|
|
@ -49,8 +78,8 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
Assert.Equal(FileChangeKind.Changed, args.Kind);
|
Assert.Equal(FileChangeKind.Changed, args.Kind);
|
||||||
Assert.Collection(
|
Assert.Collection(
|
||||||
args.AssociatedDocuments,
|
args.AssociatedDocuments,
|
||||||
f => Assert.Equal(filePath, f),
|
f => Assert.Equal("C:\\path\\to\\project\\Views\\Home\\file.cshtml", f),
|
||||||
f => Assert.Equal(anotherFilePath, f));
|
f => Assert.Equal("C:\\path\\to\\project\\anotherFile.cshtml", f));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -59,25 +88,5 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(called);
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
using System.IO;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Microsoft.CodeAnalysis.Razor;
|
using Microsoft.CodeAnalysis.Razor;
|
||||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||||
|
|
@ -13,23 +13,48 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
public class DefaultImportDocumentManagerTest : ForegroundDispatcherTestBase
|
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]
|
[ForegroundFact]
|
||||||
public void OnSubscribed_StartsFileChangeTrackers()
|
public void OnSubscribed_StartsFileChangeTrackers()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var filePath = "C:\\path\\to\\project\\Views\\Home\\file.cshtml";
|
var tracker = Mock.Of<VisualStudioDocumentTracker>(
|
||||||
var projectPath = "C:\\path\\to\\project\\project.csproj";
|
t => t.FilePath == "C:\\path\\to\\project\\Views\\Home\\file.cshtml" &&
|
||||||
var tracker = Mock.Of<VisualStudioDocumentTracker>(t => t.FilePath == filePath && t.ProjectPath == projectPath);
|
t.ProjectPath == ProjectPath &&
|
||||||
var projectEngineService = GetProjectEngineFactoryService();
|
t.ProjectSnapshot == Mock.Of<ProjectSnapshot>(p => p.GetProjectEngine() == ProjectEngine));
|
||||||
var fileChangeTracker1 = new Mock<FileChangeTracker>();
|
|
||||||
fileChangeTracker1.Setup(f => f.StartListening()).Verifiable();
|
|
||||||
var fileChangeTrackerFactory = new Mock<FileChangeTrackerFactory>();
|
var fileChangeTrackerFactory = new Mock<FileChangeTrackerFactory>();
|
||||||
|
var fileChangeTracker1 = new Mock<FileChangeTracker>();
|
||||||
|
fileChangeTracker1
|
||||||
|
.Setup(f => f.StartListening())
|
||||||
|
.Verifiable();
|
||||||
fileChangeTrackerFactory
|
fileChangeTrackerFactory
|
||||||
.Setup(f => f.Create("C:\\path\\to\\project\\Views\\Home\\_ViewImports.cshtml"))
|
.Setup(f => f.Create("C:\\path\\to\\project\\Views\\Home\\_ViewImports.cshtml"))
|
||||||
.Returns(fileChangeTracker1.Object)
|
.Returns(fileChangeTracker1.Object)
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
var fileChangeTracker2 = new Mock<FileChangeTracker>();
|
var fileChangeTracker2 = new Mock<FileChangeTracker>();
|
||||||
fileChangeTracker2.Setup(f => f.StartListening()).Verifiable();
|
fileChangeTracker2
|
||||||
|
.Setup(f => f.StartListening())
|
||||||
|
.Verifiable();
|
||||||
fileChangeTrackerFactory
|
fileChangeTrackerFactory
|
||||||
.Setup(f => f.Create("C:\\path\\to\\project\\Views\\_ViewImports.cshtml"))
|
.Setup(f => f.Create("C:\\path\\to\\project\\Views\\_ViewImports.cshtml"))
|
||||||
.Returns(fileChangeTracker2.Object)
|
.Returns(fileChangeTracker2.Object)
|
||||||
|
|
@ -41,7 +66,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
.Returns(fileChangeTracker3.Object)
|
.Returns(fileChangeTracker3.Object)
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
|
|
||||||
var manager = new DefaultImportDocumentManager(Dispatcher, new DefaultErrorReporter(), fileChangeTrackerFactory.Object, projectEngineService);
|
var manager = new DefaultImportDocumentManager(Dispatcher, new DefaultErrorReporter(), fileChangeTrackerFactory.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
manager.OnSubscribed(tracker);
|
manager.OnSubscribed(tracker);
|
||||||
|
|
@ -57,10 +82,15 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
public void OnSubscribed_AlreadySubscribed_DoesNothing()
|
public void OnSubscribed_AlreadySubscribed_DoesNothing()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var filePath = "C:\\path\\to\\project\\file.cshtml";
|
var tracker = Mock.Of<VisualStudioDocumentTracker>(
|
||||||
var projectPath = "C:\\path\\to\\project\\project.csproj";
|
t => t.FilePath == "C:\\path\\to\\project\\file.cshtml" &&
|
||||||
var tracker = Mock.Of<VisualStudioDocumentTracker>(t => t.FilePath == filePath && t.ProjectPath == projectPath);
|
t.ProjectPath == ProjectPath &&
|
||||||
var projectEngineService = GetProjectEngineFactoryService();
|
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 callCount = 0;
|
||||||
var fileChangeTrackerFactory = new Mock<FileChangeTrackerFactory>();
|
var fileChangeTrackerFactory = new Mock<FileChangeTrackerFactory>();
|
||||||
|
|
@ -69,12 +99,9 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
.Returns(Mock.Of<FileChangeTracker>())
|
.Returns(Mock.Of<FileChangeTracker>())
|
||||||
.Callback(() => callCount++);
|
.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.
|
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
|
// Act
|
||||||
manager.OnSubscribed(anotherTracker);
|
manager.OnSubscribed(anotherTracker);
|
||||||
|
|
||||||
|
|
@ -86,20 +113,22 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
public void OnUnsubscribed_StopsFileChangeTracker()
|
public void OnUnsubscribed_StopsFileChangeTracker()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var filePath = "C:\\path\\to\\project\\file.cshtml";
|
var tracker = Mock.Of<VisualStudioDocumentTracker>(
|
||||||
var projectPath = "C:\\path\\to\\project\\project.csproj";
|
t => t.FilePath == "C:\\path\\to\\project\\file.cshtml" &&
|
||||||
var tracker = Mock.Of<VisualStudioDocumentTracker>(t => t.FilePath == filePath && t.ProjectPath == projectPath);
|
t.ProjectPath == ProjectPath &&
|
||||||
var projectEngineService = GetProjectEngineFactoryService();
|
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 fileChangeTrackerFactory = new Mock<FileChangeTrackerFactory>(MockBehavior.Strict);
|
||||||
|
var fileChangeTracker = new Mock<FileChangeTracker>();
|
||||||
|
fileChangeTracker
|
||||||
|
.Setup(f => f.StopListening())
|
||||||
|
.Verifiable();
|
||||||
fileChangeTrackerFactory
|
fileChangeTrackerFactory
|
||||||
.Setup(f => f.Create("C:\\path\\to\\project\\_ViewImports.cshtml"))
|
.Setup(f => f.Create("C:\\path\\to\\project\\_ViewImports.cshtml"))
|
||||||
.Returns(fileChangeTracker.Object)
|
.Returns(fileChangeTracker.Object)
|
||||||
.Verifiable();
|
.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.
|
manager.OnSubscribed(tracker); // Start tracking the import.
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -114,49 +143,33 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
public void OnUnsubscribed_AnotherDocumentTrackingImport_DoesNotStopFileChangeTracker()
|
public void OnUnsubscribed_AnotherDocumentTrackingImport_DoesNotStopFileChangeTracker()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var filePath = "C:\\path\\to\\project\\file.cshtml";
|
var tracker = Mock.Of<VisualStudioDocumentTracker>(
|
||||||
var projectPath = "C:\\path\\to\\project\\project.csproj";
|
t => t.FilePath == "C:\\path\\to\\project\\file.cshtml" &&
|
||||||
var tracker = Mock.Of<VisualStudioDocumentTracker>(t => t.FilePath == filePath && t.ProjectPath == projectPath);
|
t.ProjectPath == ProjectPath &&
|
||||||
var projectEngineService = GetProjectEngineFactoryService();
|
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>();
|
var fileChangeTracker = new Mock<FileChangeTracker>();
|
||||||
fileChangeTracker
|
fileChangeTracker
|
||||||
.Setup(f => f.StopListening())
|
.Setup(f => f.StopListening())
|
||||||
.Throws(new InvalidOperationException());
|
.Throws(new InvalidOperationException());
|
||||||
var fileChangeTrackerFactory = new Mock<FileChangeTrackerFactory>();
|
|
||||||
fileChangeTrackerFactory
|
fileChangeTrackerFactory
|
||||||
.Setup(f => f.Create(It.IsAny<string>()))
|
.Setup(f => f.Create(It.IsAny<string>()))
|
||||||
.Returns(fileChangeTracker.Object);
|
.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.
|
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.
|
manager.OnSubscribed(anotherTracker); // Starts tracking import for the second document.
|
||||||
|
|
||||||
// Act & Assert (Does not throw)
|
// Act & Assert (Does not throw)
|
||||||
manager.OnUnsubscribed(tracker);
|
manager.OnUnsubscribed(tracker);
|
||||||
}
|
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,19 +5,19 @@ using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Microsoft.CodeAnalysis;
|
|
||||||
using Microsoft.CodeAnalysis.Razor;
|
|
||||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||||
using Moq;
|
using Microsoft.VisualStudio.Editor.Razor;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
using Mvc1_X = Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X;
|
using Mvc1_X = Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X;
|
||||||
using MvcLatest = Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
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;
|
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_0 = new HostProject("/TestPath/SomePath/Test.csproj", FallbackRazorConfiguration.MVC_2_0);
|
||||||
|
|
||||||
HostProject_For_2_1 = new HostProject(
|
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>()));
|
new ProjectSystemRazorConfiguration(RazorLanguageVersion.Version_2_1, "MVC-2.1", Array.Empty<RazorExtension>()));
|
||||||
HostProject_For_UnknownConfiguration = new HostProject(
|
HostProject_For_UnknownConfiguration = new HostProject(
|
||||||
"/TestPath/SomePath/Test.csproj",
|
"/TestPath/SomePath/Test.csproj",
|
||||||
new ProjectSystemRazorConfiguration(RazorLanguageVersion.Version_2_1, "Blazor-0.1", Array.Empty<RazorExtension>()));
|
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>[]
|
CustomFactories = new Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[]
|
||||||
{
|
{
|
||||||
|
|
@ -74,7 +79,16 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
|
|
||||||
private HostProject HostProject_For_UnknownConfiguration { get; }
|
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 Project WorkspaceProject { get; }
|
||||||
|
|
||||||
private Workspace Workspace { get; }
|
private Workspace Workspace { get; }
|
||||||
|
|
@ -83,14 +97,12 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
public void Create_CreatesDesignTimeTemplateEngine_ForVersion2_1()
|
public void Create_CreatesDesignTimeTemplateEngine_ForVersion2_1()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
var snapshot = Snapshot_For_2_1;
|
||||||
projectManager.HostProjectAdded(HostProject_For_2_1);
|
|
||||||
projectManager.WorkspaceProjectAdded(WorkspaceProject);
|
|
||||||
|
|
||||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager, FallbackFactory, CustomFactories);
|
var factory = new DefaultProjectSnapshotProjectEngineFactory(FallbackFactory, CustomFactories);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var engine = factoryService.Create("/TestPath/SomePath/", b =>
|
var engine = factory.Create(snapshot, b =>
|
||||||
{
|
{
|
||||||
b.Features.Add(new MyCoolNewFeature());
|
b.Features.Add(new MyCoolNewFeature());
|
||||||
});
|
});
|
||||||
|
|
@ -106,14 +118,12 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
public void Create_CreatesDesignTimeTemplateEngine_ForVersion2_0()
|
public void Create_CreatesDesignTimeTemplateEngine_ForVersion2_0()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
var snapshot = Snapshot_For_2_0;
|
||||||
projectManager.HostProjectAdded(HostProject_For_2_0);
|
|
||||||
projectManager.WorkspaceProjectAdded(WorkspaceProject);
|
|
||||||
|
|
||||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager, FallbackFactory, CustomFactories);
|
var factory = new DefaultProjectSnapshotProjectEngineFactory(FallbackFactory, CustomFactories);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var engine = factoryService.Create("/TestPath/SomePath/", b =>
|
var engine = factory.Create(snapshot, b =>
|
||||||
{
|
{
|
||||||
b.Features.Add(new MyCoolNewFeature());
|
b.Features.Add(new MyCoolNewFeature());
|
||||||
});
|
});
|
||||||
|
|
@ -129,14 +139,12 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
public void Create_CreatesTemplateEngine_ForVersion1_1()
|
public void Create_CreatesTemplateEngine_ForVersion1_1()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
var snapshot = Snapshot_For_1_1;
|
||||||
projectManager.HostProjectAdded(HostProject_For_1_1);
|
|
||||||
projectManager.WorkspaceProjectAdded(WorkspaceProject);
|
|
||||||
|
|
||||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager, FallbackFactory, CustomFactories);
|
var factory = new DefaultProjectSnapshotProjectEngineFactory(FallbackFactory, CustomFactories);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var engine = factoryService.Create("/TestPath/SomePath/", b =>
|
var engine = factory.Create(snapshot, b =>
|
||||||
{
|
{
|
||||||
b.Features.Add(new MyCoolNewFeature());
|
b.Features.Add(new MyCoolNewFeature());
|
||||||
});
|
});
|
||||||
|
|
@ -152,14 +160,12 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
public void Create_DoesNotSupportViewComponentTagHelpers_ForVersion1_0()
|
public void Create_DoesNotSupportViewComponentTagHelpers_ForVersion1_0()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
var snapshot = Snapshot_For_1_0;
|
||||||
projectManager.HostProjectAdded(HostProject_For_1_0);
|
|
||||||
projectManager.WorkspaceProjectAdded(WorkspaceProject);
|
|
||||||
|
|
||||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager, FallbackFactory, CustomFactories);
|
var factory = new DefaultProjectSnapshotProjectEngineFactory(FallbackFactory, CustomFactories);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var engine = factoryService.Create("/TestPath/SomePath/", b =>
|
var engine = factory.Create(snapshot, b =>
|
||||||
{
|
{
|
||||||
b.Features.Add(new MyCoolNewFeature());
|
b.Features.Add(new MyCoolNewFeature());
|
||||||
});
|
});
|
||||||
|
|
@ -171,40 +177,15 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
Assert.Empty(engine.Engine.Features.OfType<MvcLatest.ViewComponentTagHelperPass>());
|
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]
|
[Fact]
|
||||||
public void Create_ForUnknownConfiguration_UsesFallbackFactory()
|
public void Create_ForUnknownConfiguration_UsesFallbackFactory()
|
||||||
{
|
{
|
||||||
// Arrange
|
var snapshot = Snapshot_For_UnknownConfiguration;
|
||||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
|
||||||
projectManager.HostProjectAdded(HostProject_For_UnknownConfiguration);
|
|
||||||
projectManager.WorkspaceProjectAdded(WorkspaceProject);
|
|
||||||
|
|
||||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager, FallbackFactory, CustomFactories);
|
var factory = new DefaultProjectSnapshotProjectEngineFactory(FallbackFactory, CustomFactories);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var engine = factoryService.Create("/TestPath/SomePath/", b =>
|
var engine = factory.Create(snapshot, b =>
|
||||||
{
|
{
|
||||||
b.Features.Add(new MyCoolNewFeature());
|
b.Features.Add(new MyCoolNewFeature());
|
||||||
});
|
});
|
||||||
|
|
@ -221,18 +202,5 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
public RazorEngine Engine { get; set; }
|
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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.Host;
|
||||||
using Microsoft.CodeAnalysis.Razor;
|
using Microsoft.CodeAnalysis.Razor;
|
||||||
using Microsoft.CodeAnalysis.Razor.Editor;
|
using Microsoft.CodeAnalysis.Razor.Editor;
|
||||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||||
|
|
@ -18,29 +20,90 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
public class DefaultVisualStudioDocumentTrackerTest : ForegroundDispatcherTestBase
|
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()
|
public void EditorSettingsManager_Changed_TriggersContextChanged()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager);
|
|
||||||
var called = false;
|
var called = false;
|
||||||
documentTracker.ContextChanged += (sender, args) =>
|
DocumentTracker.ContextChanged += (sender, args) =>
|
||||||
{
|
{
|
||||||
Assert.Equal(ContextChangeKind.EditorSettingsChanged, args.Kind);
|
Assert.Equal(ContextChangeKind.EditorSettingsChanged, args.Kind);
|
||||||
called = true;
|
called = true;
|
||||||
|
|
@ -48,103 +111,109 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
documentTracker.EditorSettingsManager_Changed(null, null);
|
DocumentTracker.EditorSettingsManager_Changed(null, null);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(called);
|
Assert.True(called);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[ForegroundFact]
|
||||||
public void ProjectManager_Changed_ProjectChanged_TriggersContextChanged()
|
public void ProjectManager_Changed_ProjectAdded_TriggersContextChanged()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
Project project = null;
|
ProjectManager.HostProjectAdded(HostProject);
|
||||||
var workspace = TestWorkspace.Create(ws =>
|
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
var projectSnapshot = new DefaultProjectSnapshot(new HostProject(project.FilePath, RazorConfiguration.Default), project);
|
var e = new ProjectChangeEventArgs(ProjectPath, ProjectChangeKind.ProjectAdded);
|
||||||
var projectChangedArgs = new ProjectChangeEventArgs(projectSnapshot, ProjectChangeKind.Changed);
|
|
||||||
|
|
||||||
var called = false;
|
var called = false;
|
||||||
documentTracker.ContextChanged += (sender, args) =>
|
DocumentTracker.ContextChanged += (sender, args) =>
|
||||||
{
|
{
|
||||||
Assert.Equal(ContextChangeKind.ProjectChanged, args.Kind);
|
Assert.Equal(ContextChangeKind.ProjectChanged, args.Kind);
|
||||||
called = true;
|
called = true;
|
||||||
|
|
||||||
|
Assert.Same(ProjectManager.GetLoadedProject(DocumentTracker.ProjectPath), DocumentTracker.ProjectSnapshot);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
documentTracker.ProjectManager_Changed(null, projectChangedArgs);
|
DocumentTracker.ProjectManager_Changed(ProjectManager, e);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(called);
|
Assert.True(called);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[ForegroundFact]
|
||||||
public void ProjectManager_Changed_TagHelpersChanged_TriggersContextChanged()
|
public void ProjectManager_Changed_ProjectChanged_TriggersContextChanged()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
Project project = null;
|
ProjectManager.HostProjectAdded(HostProject);
|
||||||
var workspace = TestWorkspace.Create(ws =>
|
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
var projectSnapshot = new DefaultProjectSnapshot(new HostProject(project.FilePath, RazorConfiguration.Default), project);
|
var e = new ProjectChangeEventArgs(ProjectPath, ProjectChangeKind.ProjectChanged);
|
||||||
var projectChangedArgs = new ProjectChangeEventArgs(projectSnapshot, ProjectChangeKind.TagHelpersChanged);
|
|
||||||
|
|
||||||
var called = false;
|
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;
|
called = true;
|
||||||
|
|
||||||
|
Assert.Same(ProjectManager.GetLoadedProject(DocumentTracker.ProjectPath), DocumentTracker.ProjectSnapshot);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
documentTracker.ProjectManager_Changed(null, projectChangedArgs);
|
DocumentTracker.ProjectManager_Changed(ProjectManager, e);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(called);
|
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()
|
public void ProjectManager_Changed_IgnoresUnknownProject()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
Project project = null;
|
var e = new ProjectChangeEventArgs("c:/OtherPath/OtherProject.csproj", ProjectChangeKind.ProjectChanged);
|
||||||
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 called = false;
|
var called = false;
|
||||||
documentTracker.ContextChanged += (sender, args) =>
|
DocumentTracker.ContextChanged += (sender, args) =>
|
||||||
{
|
{
|
||||||
called = true;
|
called = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
documentTracker.ProjectManager_Changed(null, projectChangedArgs);
|
DocumentTracker.ProjectManager_Changed(ProjectManager, e);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(called);
|
Assert.False(called);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[ForegroundFact]
|
||||||
public void Import_Changed_ImportAssociatedWithDocument_TriggersContextChanged()
|
public void Import_Changed_ImportAssociatedWithDocument_TriggersContextChanged()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager);
|
|
||||||
|
|
||||||
var called = false;
|
var called = false;
|
||||||
documentTracker.ContextChanged += (sender, args) =>
|
DocumentTracker.ContextChanged += (sender, args) =>
|
||||||
{
|
{
|
||||||
Assert.Equal(ContextChangeKind.ImportsChanged, args.Kind);
|
Assert.Equal(ContextChangeKind.ImportsChanged, args.Kind);
|
||||||
called = true;
|
called = true;
|
||||||
|
|
@ -153,19 +222,17 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
var importChangedArgs = new ImportChangedEventArgs("path/to/import", FileChangeKind.Changed, new[] { FilePath });
|
var importChangedArgs = new ImportChangedEventArgs("path/to/import", FileChangeKind.Changed, new[] { FilePath });
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
documentTracker.Import_Changed(null, importChangedArgs);
|
DocumentTracker.Import_Changed(null, importChangedArgs);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(called);
|
Assert.True(called);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[ForegroundFact]
|
||||||
public void Import_Changed_UnrelatedImport_DoesNothing()
|
public void Import_Changed_UnrelatedImport_DoesNothing()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager);
|
DocumentTracker.ContextChanged += (sender, args) =>
|
||||||
|
|
||||||
documentTracker.ContextChanged += (sender, args) =>
|
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException();
|
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" });
|
var importChangedArgs = new ImportChangedEventArgs("path/to/import", FileChangeKind.Changed, new[] { "path/to/differentfile" });
|
||||||
|
|
||||||
// Act & Assert (Does not throw)
|
// Act & Assert (Does not throw)
|
||||||
documentTracker.Import_Changed(null, importChangedArgs);
|
DocumentTracker.Import_Changed(null, importChangedArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[ForegroundFact]
|
||||||
public void Subscribe_SetsSupportedProjectAndTriggersContextChanged()
|
public void Subscribe_SetsSupportedProjectAndTriggersContextChanged()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager);
|
|
||||||
var called = false;
|
var called = false;
|
||||||
documentTracker.ContextChanged += (sender, args) =>
|
DocumentTracker.ContextChanged += (sender, args) =>
|
||||||
{
|
{
|
||||||
called = true;
|
called = true; // This will trigger both ContextChanged and TagHelprsChanged
|
||||||
Assert.Equal(ContextChangeKind.ProjectChanged, args.Kind);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
documentTracker.Subscribe();
|
DocumentTracker.Subscribe();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(called);
|
Assert.True(called);
|
||||||
Assert.True(documentTracker.IsSupportedProject);
|
Assert.True(DocumentTracker.IsSupportedProject);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[ForegroundFact]
|
||||||
public void Unsubscribe_ResetsSupportedProjectAndTriggersContextChanged()
|
public void Unsubscribe_ResetsSupportedProjectAndTriggersContextChanged()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager);
|
|
||||||
|
|
||||||
// Subscribe once to set supported project
|
// Subscribe once to set supported project
|
||||||
documentTracker.Subscribe();
|
DocumentTracker.Subscribe();
|
||||||
|
|
||||||
var called = false;
|
var called = false;
|
||||||
documentTracker.ContextChanged += (sender, args) =>
|
DocumentTracker.ContextChanged += (sender, args) =>
|
||||||
{
|
{
|
||||||
called = true;
|
called = true;
|
||||||
Assert.Equal(ContextChangeKind.ProjectChanged, args.Kind);
|
Assert.Equal(ContextChangeKind.ProjectChanged, args.Kind);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
documentTracker.Unsubscribe();
|
DocumentTracker.Unsubscribe();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(documentTracker.IsSupportedProject);
|
Assert.False(DocumentTracker.IsSupportedProject);
|
||||||
Assert.True(called);
|
Assert.True(called);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[ForegroundFact]
|
||||||
public void AddTextView_AddsToTextViewCollection()
|
public void AddTextView_AddsToTextViewCollection()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager);
|
|
||||||
var textView = Mock.Of<ITextView>();
|
var textView = Mock.Of<ITextView>();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
documentTracker.AddTextView(textView);
|
DocumentTracker.AddTextView(textView);
|
||||||
|
|
||||||
// Assert
|
// 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()
|
public void AddTextView_DoesNotAddDuplicateTextViews()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager);
|
|
||||||
var textView = Mock.Of<ITextView>();
|
var textView = Mock.Of<ITextView>();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
documentTracker.AddTextView(textView);
|
DocumentTracker.AddTextView(textView);
|
||||||
documentTracker.AddTextView(textView);
|
DocumentTracker.AddTextView(textView);
|
||||||
|
|
||||||
// Assert
|
// 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()
|
public void AddTextView_AddsMultipleTextViewsToCollection()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager);
|
|
||||||
var textView1 = Mock.Of<ITextView>();
|
var textView1 = Mock.Of<ITextView>();
|
||||||
var textView2 = Mock.Of<ITextView>();
|
var textView2 = Mock.Of<ITextView>();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
documentTracker.AddTextView(textView1);
|
DocumentTracker.AddTextView(textView1);
|
||||||
documentTracker.AddTextView(textView2);
|
DocumentTracker.AddTextView(textView2);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(
|
Assert.Collection(
|
||||||
documentTracker.TextViews,
|
DocumentTracker.TextViews,
|
||||||
v => Assert.Same(v, textView1),
|
v => Assert.Same(v, textView1),
|
||||||
v => Assert.Same(v, textView2));
|
v => Assert.Same(v, textView2));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[ForegroundFact]
|
||||||
public void RemoveTextView_RemovesTextViewFromCollection_SingleItem()
|
public void RemoveTextView_RemovesTextViewFromCollection_SingleItem()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager);
|
|
||||||
var textView = Mock.Of<ITextView>();
|
var textView = Mock.Of<ITextView>();
|
||||||
documentTracker.AddTextView(textView);
|
DocumentTracker.AddTextView(textView);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
documentTracker.RemoveTextView(textView);
|
DocumentTracker.RemoveTextView(textView);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Empty(documentTracker.TextViews);
|
Assert.Empty(DocumentTracker.TextViews);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[ForegroundFact]
|
||||||
public void RemoveTextView_RemovesTextViewFromCollection_MultipleItems()
|
public void RemoveTextView_RemovesTextViewFromCollection_MultipleItems()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager);
|
|
||||||
var textView1 = Mock.Of<ITextView>();
|
var textView1 = Mock.Of<ITextView>();
|
||||||
var textView2 = Mock.Of<ITextView>();
|
var textView2 = Mock.Of<ITextView>();
|
||||||
var textView3 = Mock.Of<ITextView>();
|
var textView3 = Mock.Of<ITextView>();
|
||||||
documentTracker.AddTextView(textView1);
|
DocumentTracker.AddTextView(textView1);
|
||||||
documentTracker.AddTextView(textView2);
|
DocumentTracker.AddTextView(textView2);
|
||||||
documentTracker.AddTextView(textView3);
|
DocumentTracker.AddTextView(textView3);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
documentTracker.RemoveTextView(textView2);
|
DocumentTracker.RemoveTextView(textView2);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(
|
Assert.Collection(
|
||||||
documentTracker.TextViews,
|
DocumentTracker.TextViews,
|
||||||
v => Assert.Same(v, textView1),
|
v => Assert.Same(v, textView1),
|
||||||
v => Assert.Same(v, textView3));
|
v => Assert.Same(v, textView3));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[ForegroundFact]
|
||||||
public void RemoveTextView_NoopsWhenRemovingTextViewNotInCollection()
|
public void RemoveTextView_NoopsWhenRemovingTextViewNotInCollection()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager);
|
|
||||||
var textView1 = Mock.Of<ITextView>();
|
var textView1 = Mock.Of<ITextView>();
|
||||||
documentTracker.AddTextView(textView1);
|
DocumentTracker.AddTextView(textView1);
|
||||||
var textView2 = Mock.Of<ITextView>();
|
var textView2 = Mock.Of<ITextView>();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
documentTracker.RemoveTextView(textView2);
|
DocumentTracker.RemoveTextView(textView2);
|
||||||
|
|
||||||
// Assert
|
// 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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.Razor;
|
using Microsoft.CodeAnalysis.Razor;
|
||||||
|
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||||
using Microsoft.VisualStudio.Test;
|
using Microsoft.VisualStudio.Test;
|
||||||
using Microsoft.VisualStudio.Text;
|
using Microsoft.VisualStudio.Text;
|
||||||
using Microsoft.VisualStudio.Text.Editor;
|
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 TestLinePragmaFileName = "C:\\This\\Path\\Is\\Just\\For\\Line\\Pragmas.cshtml";
|
||||||
private const string TestProjectPath = "C:\\This\\Path\\Is\\Just\\For\\Project.csproj";
|
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]
|
[ForegroundFact]
|
||||||
public async Task BufferChangeStartsFullReparseIfChangeOverlapsMultipleSpans()
|
public async Task BufferChangeStartsFullReparseIfChangeOverlapsMultipleSpans()
|
||||||
{
|
{
|
||||||
|
|
@ -505,7 +516,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
var textBuffer = new TestTextBuffer(originalSnapshot);
|
var textBuffer = new TestTextBuffer(originalSnapshot);
|
||||||
var documentTracker = CreateDocumentTracker(textBuffer);
|
var documentTracker = CreateDocumentTracker(textBuffer);
|
||||||
var templateEngineFactory = CreateTemplateEngineFactory();
|
var templateEngineFactory = CreateProjectEngineFactory();
|
||||||
var parser = new DefaultVisualStudioRazorParser(
|
var parser = new DefaultVisualStudioRazorParser(
|
||||||
Dispatcher,
|
Dispatcher,
|
||||||
documentTracker,
|
documentTracker,
|
||||||
|
|
@ -524,7 +535,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
return new TestParserManager(parser);
|
return new TestParserManager(parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static RazorProjectEngineFactoryService CreateTemplateEngineFactory(
|
private static ProjectSnapshotProjectEngineFactory CreateProjectEngineFactory(
|
||||||
string path = TestLinePragmaFileName,
|
string path = TestLinePragmaFileName,
|
||||||
IEnumerable<TagHelperDescriptor> tagHelpers = null)
|
IEnumerable<TagHelperDescriptor> tagHelpers = null)
|
||||||
{
|
{
|
||||||
|
|
@ -541,10 +552,10 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var projectEngineFactoryService = Mock.Of<RazorProjectEngineFactoryService>(
|
return new TestProjectSnapshotProjectEngineFactory()
|
||||||
service => service.Create(It.IsAny<string>(), It.IsAny<Action<RazorProjectEngineBuilder>>()) == projectEngine);
|
{
|
||||||
|
Engine = projectEngine,
|
||||||
return projectEngineFactoryService;
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RunTypeKeywordTestAsync(string keyword)
|
private async Task RunTypeKeywordTestAsync(string keyword)
|
||||||
|
|
@ -584,7 +595,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private static VisualStudioDocumentTracker CreateDocumentTracker(Text.ITextBuffer textBuffer)
|
private VisualStudioDocumentTracker CreateDocumentTracker(Text.ITextBuffer textBuffer)
|
||||||
{
|
{
|
||||||
var focusedTextView = Mock.Of<ITextView>(textView => textView.HasAggregateFocus == true);
|
var focusedTextView = Mock.Of<ITextView>(textView => textView.HasAggregateFocus == true);
|
||||||
var documentTracker = Mock.Of<VisualStudioDocumentTracker>(tracker =>
|
var documentTracker = Mock.Of<VisualStudioDocumentTracker>(tracker =>
|
||||||
|
|
@ -592,6 +603,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
tracker.TextViews == new[] { focusedTextView } &&
|
tracker.TextViews == new[] { focusedTextView } &&
|
||||||
tracker.FilePath == TestLinePragmaFileName &&
|
tracker.FilePath == TestLinePragmaFileName &&
|
||||||
tracker.ProjectPath == TestProjectPath &&
|
tracker.ProjectPath == TestProjectPath &&
|
||||||
|
tracker.ProjectSnapshot == ProjectSnapshot &&
|
||||||
tracker.IsSupportedProject == true);
|
tracker.IsSupportedProject == true);
|
||||||
textBuffer.Properties.AddProperty(typeof(VisualStudioDocumentTracker), documentTracker);
|
textBuffer.Properties.AddProperty(typeof(VisualStudioDocumentTracker), documentTracker);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.Razor;
|
using Microsoft.CodeAnalysis.Razor;
|
||||||
|
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||||
using Microsoft.VisualStudio.Test;
|
using Microsoft.VisualStudio.Test;
|
||||||
using Microsoft.VisualStudio.Text;
|
using Microsoft.VisualStudio.Text;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
|
@ -14,12 +16,32 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
public class DefaultVisualStudioRazorParserTest : ForegroundDispatcherTestBase
|
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 =>
|
var documentTracker = Mock.Of<VisualStudioDocumentTracker>(tracker =>
|
||||||
tracker.TextBuffer == new TestTextBuffer(new StringTextSnapshot(string.Empty)) &&
|
tracker.TextBuffer == new TestTextBuffer(new StringTextSnapshot(string.Empty)) &&
|
||||||
tracker.ProjectPath == "SomeProject.csproj" &&
|
tracker.ProjectPath == "c:\\SomeProject.csproj" &&
|
||||||
tracker.FilePath == "SomeFilePath.cshtml" &&
|
tracker.ProjectSnapshot == ProjectSnapshot &&
|
||||||
|
tracker.FilePath == "c:\\SomeFilePath.cshtml" &&
|
||||||
tracker.IsSupportedProject == isSupportedProject);
|
tracker.IsSupportedProject == isSupportedProject);
|
||||||
|
|
||||||
return documentTracker;
|
return documentTracker;
|
||||||
|
|
@ -32,7 +54,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
var parser = new DefaultVisualStudioRazorParser(
|
var parser = new DefaultVisualStudioRazorParser(
|
||||||
Dispatcher,
|
Dispatcher,
|
||||||
CreateDocumentTracker(),
|
CreateDocumentTracker(),
|
||||||
Mock.Of<RazorProjectEngineFactoryService>(),
|
ProjectEngineFactory,
|
||||||
new DefaultErrorReporter(),
|
new DefaultErrorReporter(),
|
||||||
Mock.Of<VisualStudioCompletionBroker>());
|
Mock.Of<VisualStudioCompletionBroker>());
|
||||||
parser.Dispose();
|
parser.Dispose();
|
||||||
|
|
@ -48,7 +70,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
var parser = new DefaultVisualStudioRazorParser(
|
var parser = new DefaultVisualStudioRazorParser(
|
||||||
Dispatcher,
|
Dispatcher,
|
||||||
CreateDocumentTracker(),
|
CreateDocumentTracker(),
|
||||||
Mock.Of<RazorProjectEngineFactoryService>(),
|
ProjectEngineFactory,
|
||||||
new DefaultErrorReporter(),
|
new DefaultErrorReporter(),
|
||||||
Mock.Of<VisualStudioCompletionBroker>());
|
Mock.Of<VisualStudioCompletionBroker>());
|
||||||
parser.Dispose();
|
parser.Dispose();
|
||||||
|
|
@ -64,7 +86,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
var parser = new DefaultVisualStudioRazorParser(
|
var parser = new DefaultVisualStudioRazorParser(
|
||||||
Dispatcher,
|
Dispatcher,
|
||||||
CreateDocumentTracker(),
|
CreateDocumentTracker(),
|
||||||
Mock.Of<RazorProjectEngineFactoryService>(),
|
ProjectEngineFactory,
|
||||||
new DefaultErrorReporter(),
|
new DefaultErrorReporter(),
|
||||||
Mock.Of<VisualStudioCompletionBroker>());
|
Mock.Of<VisualStudioCompletionBroker>());
|
||||||
parser.Dispose();
|
parser.Dispose();
|
||||||
|
|
@ -80,7 +102,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
using (var parser = new DefaultVisualStudioRazorParser(
|
using (var parser = new DefaultVisualStudioRazorParser(
|
||||||
Dispatcher,
|
Dispatcher,
|
||||||
CreateDocumentTracker(),
|
CreateDocumentTracker(),
|
||||||
Mock.Of<RazorProjectEngineFactoryService>(),
|
ProjectEngineFactory,
|
||||||
new DefaultErrorReporter(),
|
new DefaultErrorReporter(),
|
||||||
Mock.Of<VisualStudioCompletionBroker>()))
|
Mock.Of<VisualStudioCompletionBroker>()))
|
||||||
{
|
{
|
||||||
|
|
@ -108,7 +130,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
using (var parser = new DefaultVisualStudioRazorParser(
|
using (var parser = new DefaultVisualStudioRazorParser(
|
||||||
Dispatcher,
|
Dispatcher,
|
||||||
documentTracker,
|
documentTracker,
|
||||||
Mock.Of<RazorProjectEngineFactoryService>(),
|
ProjectEngineFactory,
|
||||||
new DefaultErrorReporter(),
|
new DefaultErrorReporter(),
|
||||||
Mock.Of<VisualStudioCompletionBroker>()))
|
Mock.Of<VisualStudioCompletionBroker>()))
|
||||||
{
|
{
|
||||||
|
|
@ -139,7 +161,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
using (var parser = new DefaultVisualStudioRazorParser(
|
using (var parser = new DefaultVisualStudioRazorParser(
|
||||||
Dispatcher,
|
Dispatcher,
|
||||||
CreateDocumentTracker(),
|
CreateDocumentTracker(),
|
||||||
Mock.Of<RazorProjectEngineFactoryService>(),
|
ProjectEngineFactory,
|
||||||
new DefaultErrorReporter(),
|
new DefaultErrorReporter(),
|
||||||
Mock.Of<VisualStudioCompletionBroker>())
|
Mock.Of<VisualStudioCompletionBroker>())
|
||||||
{
|
{
|
||||||
|
|
@ -169,7 +191,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
using (var parser = new DefaultVisualStudioRazorParser(
|
using (var parser = new DefaultVisualStudioRazorParser(
|
||||||
Dispatcher,
|
Dispatcher,
|
||||||
CreateDocumentTracker(),
|
CreateDocumentTracker(),
|
||||||
Mock.Of<RazorProjectEngineFactoryService>(),
|
ProjectEngineFactory,
|
||||||
new DefaultErrorReporter(),
|
new DefaultErrorReporter(),
|
||||||
Mock.Of<VisualStudioCompletionBroker>())
|
Mock.Of<VisualStudioCompletionBroker>())
|
||||||
{
|
{
|
||||||
|
|
@ -198,7 +220,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
using (var parser = new DefaultVisualStudioRazorParser(
|
using (var parser = new DefaultVisualStudioRazorParser(
|
||||||
Dispatcher,
|
Dispatcher,
|
||||||
CreateDocumentTracker(),
|
CreateDocumentTracker(),
|
||||||
Mock.Of<RazorProjectEngineFactoryService>(),
|
ProjectEngineFactory,
|
||||||
new DefaultErrorReporter(),
|
new DefaultErrorReporter(),
|
||||||
Mock.Of<VisualStudioCompletionBroker>()))
|
Mock.Of<VisualStudioCompletionBroker>()))
|
||||||
{
|
{
|
||||||
|
|
@ -222,7 +244,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
using (var parser = new DefaultVisualStudioRazorParser(
|
using (var parser = new DefaultVisualStudioRazorParser(
|
||||||
Dispatcher,
|
Dispatcher,
|
||||||
documentTracker,
|
documentTracker,
|
||||||
Mock.Of<RazorProjectEngineFactoryService>(),
|
ProjectEngineFactory,
|
||||||
new DefaultErrorReporter(),
|
new DefaultErrorReporter(),
|
||||||
Mock.Of<VisualStudioCompletionBroker>()))
|
Mock.Of<VisualStudioCompletionBroker>()))
|
||||||
{
|
{
|
||||||
|
|
@ -242,7 +264,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
using (var parser = new DefaultVisualStudioRazorParser(
|
using (var parser = new DefaultVisualStudioRazorParser(
|
||||||
Dispatcher,
|
Dispatcher,
|
||||||
CreateDocumentTracker(isSupportedProject: true),
|
CreateDocumentTracker(isSupportedProject: true),
|
||||||
Mock.Of<RazorProjectEngineFactoryService>(),
|
ProjectEngineFactory,
|
||||||
new DefaultErrorReporter(),
|
new DefaultErrorReporter(),
|
||||||
Mock.Of<VisualStudioCompletionBroker>()))
|
Mock.Of<VisualStudioCompletionBroker>()))
|
||||||
{
|
{
|
||||||
|
|
@ -261,7 +283,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
using (var parser = new DefaultVisualStudioRazorParser(
|
using (var parser = new DefaultVisualStudioRazorParser(
|
||||||
Dispatcher,
|
Dispatcher,
|
||||||
CreateDocumentTracker(isSupportedProject: false),
|
CreateDocumentTracker(isSupportedProject: false),
|
||||||
Mock.Of<RazorProjectEngineFactoryService>(),
|
ProjectEngineFactory,
|
||||||
new DefaultErrorReporter(),
|
new DefaultErrorReporter(),
|
||||||
Mock.Of<VisualStudioCompletionBroker>()))
|
Mock.Of<VisualStudioCompletionBroker>()))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,12 @@
|
||||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="..\Microsoft.CodeAnalysis.Razor.Workspaces.Test\Shared\**\*.cs">
|
||||||
|
<Link>Shared\%(RecursiveDir)%(FileName)%(Extension)</Link>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
|
<None Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
|
||||||
</ItemGroup>
|
</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
|
// 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.
|
// 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);
|
Documents = new HostDocument[]
|
||||||
HostProject2 = new HostProject("Test2.csproj", FallbackRazorConfiguration.MVC_1_0);
|
{
|
||||||
|
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();
|
Workspace = TestWorkspace.Create();
|
||||||
|
|
||||||
|
|
@ -31,19 +37,21 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
"Test1",
|
"Test1",
|
||||||
"Test1",
|
"Test1",
|
||||||
LanguageNames.CSharp,
|
LanguageNames.CSharp,
|
||||||
"Test1.csproj"))
|
"c:\\Test1\\Test1.csproj"))
|
||||||
.AddProject(ProjectInfo.Create(
|
.AddProject(ProjectInfo.Create(
|
||||||
projectId2,
|
projectId2,
|
||||||
VersionStamp.Default,
|
VersionStamp.Default,
|
||||||
"Test2",
|
"Test2",
|
||||||
"Test2",
|
"Test2",
|
||||||
LanguageNames.CSharp,
|
LanguageNames.CSharp,
|
||||||
"Test2.csproj")); ;
|
"c:\\Test2\\Test2.csproj")); ;
|
||||||
|
|
||||||
WorkspaceProject1 = solution.GetProject(projectId1);
|
WorkspaceProject1 = solution.GetProject(projectId1);
|
||||||
WorkspaceProject2 = solution.GetProject(projectId2);
|
WorkspaceProject2 = solution.GetProject(projectId2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private HostDocument[] Documents { get; }
|
||||||
|
|
||||||
private HostProject HostProject1 { get; }
|
private HostProject HostProject1 { get; }
|
||||||
|
|
||||||
private HostProject HostProject2 { get; }
|
private HostProject HostProject2 { get; }
|
||||||
|
|
@ -63,28 +71,31 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
projectManager.HostProjectAdded(HostProject2);
|
projectManager.HostProjectAdded(HostProject2);
|
||||||
projectManager.WorkspaceProjectAdded(WorkspaceProject1);
|
projectManager.WorkspaceProjectAdded(WorkspaceProject1);
|
||||||
projectManager.WorkspaceProjectAdded(WorkspaceProject2);
|
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),
|
Delay = TimeSpan.FromMilliseconds(1),
|
||||||
BlockBackgroundWorkStart = new ManualResetEventSlim(initialState: false),
|
BlockBackgroundWorkStart = new ManualResetEventSlim(initialState: false),
|
||||||
NotifyBackgroundWorkFinish = new ManualResetEventSlim(initialState: false),
|
NotifyBackgroundWorkStarting = new ManualResetEventSlim(initialState: false),
|
||||||
NotifyForegroundWorkFinish = new ManualResetEventSlim(initialState: false),
|
BlockBackgroundWorkCompleting = new ManualResetEventSlim(initialState: false),
|
||||||
|
NotifyBackgroundWorkCompleted = new ManualResetEventSlim(initialState: false),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act & Assert
|
// 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.IsScheduledOrRunning, "Queue should be scheduled during Enqueue");
|
||||||
Assert.True(queue.HasPendingNotifications, "Queue should have a notification created 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 proceed.
|
||||||
queue.BlockBackgroundWorkStart.Set();
|
queue.BlockBackgroundWorkStart.Set();
|
||||||
|
queue.BlockBackgroundWorkCompleting.Set();
|
||||||
|
|
||||||
// Get off the foreground thread and allow the updates to flow through.
|
await Task.Run(() => queue.NotifyBackgroundWorkCompleted.Wait(TimeSpan.FromSeconds(1)));
|
||||||
await Task.Run(() => queue.NotifyForegroundWorkFinish.Wait(TimeSpan.FromSeconds(1)));
|
|
||||||
|
|
||||||
Assert.False(queue.IsScheduledOrRunning, "Queue should not have restarted");
|
Assert.False(queue.IsScheduledOrRunning, "Queue should not have restarted");
|
||||||
Assert.False(queue.HasPendingNotifications, "Queue should have processed all notifications");
|
Assert.False(queue.HasPendingNotifications, "Queue should have processed all notifications");
|
||||||
|
|
@ -99,40 +110,42 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
projectManager.HostProjectAdded(HostProject2);
|
projectManager.HostProjectAdded(HostProject2);
|
||||||
projectManager.WorkspaceProjectAdded(WorkspaceProject1);
|
projectManager.WorkspaceProjectAdded(WorkspaceProject1);
|
||||||
projectManager.WorkspaceProjectAdded(WorkspaceProject2);
|
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),
|
Delay = TimeSpan.FromMilliseconds(1),
|
||||||
BlockBackgroundWorkStart = new ManualResetEventSlim(initialState: false),
|
BlockBackgroundWorkStart = new ManualResetEventSlim(initialState: false),
|
||||||
NotifyBackgroundWorkFinish = new ManualResetEventSlim(initialState: false),
|
NotifyBackgroundWorkStarting = new ManualResetEventSlim(initialState: false),
|
||||||
NotifyForegroundWorkFinish = new ManualResetEventSlim(initialState: false),
|
BlockBackgroundWorkCompleting = new ManualResetEventSlim(initialState: false),
|
||||||
|
NotifyBackgroundWorkCompleted = new ManualResetEventSlim(initialState: false),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act & Assert
|
// 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.IsScheduledOrRunning, "Queue should be scheduled during Enqueue");
|
||||||
Assert.True(queue.HasPendingNotifications, "Queue should have a notification created 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.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.True(queue.IsScheduledOrRunning, "Worker should be processing now");
|
||||||
Assert.False(queue.HasPendingNotifications, "Worker should have taken all notifications");
|
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.
|
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.
|
// Allow work to complete, which should restart the timer.
|
||||||
await Task.Run(() => queue.NotifyForegroundWorkFinish.Wait(TimeSpan.FromSeconds(1)));
|
queue.BlockBackgroundWorkCompleting.Set();
|
||||||
|
|
||||||
queue.NotifyBackgroundWorkFinish.Reset();
|
await Task.Run(() => queue.NotifyBackgroundWorkCompleted.Wait(TimeSpan.FromSeconds(1)));
|
||||||
queue.NotifyForegroundWorkFinish.Reset();
|
queue.NotifyBackgroundWorkCompleted.Reset();
|
||||||
|
|
||||||
// It should start running again right away.
|
// It should start running again right away.
|
||||||
Assert.True(queue.IsScheduledOrRunning, "Queue should be scheduled during Enqueue");
|
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.
|
// Allow the background work to proceed.
|
||||||
queue.BlockBackgroundWorkStart.Set();
|
queue.BlockBackgroundWorkStart.Set();
|
||||||
|
|
||||||
// Get off the foreground thread and allow the updates to flow through.
|
queue.BlockBackgroundWorkCompleting.Set();
|
||||||
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.IsScheduledOrRunning, "Queue should not have restarted");
|
||||||
Assert.False(queue.HasPendingNotifications, "Queue should have processed all notifications");
|
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>
|
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="..\Microsoft.CodeAnalysis.Razor.Workspaces.Test\Shared\**\*.cs">
|
||||||
|
<Link>Shared\%(RecursiveDir)%(FileName)%(Extension)</Link>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
|
<None Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -45,12 +45,12 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
|
|
||||||
ErrorReporter = new DefaultErrorReporter();
|
ErrorReporter = new DefaultErrorReporter();
|
||||||
ProjectManager = new TestProjectSnapshotManager(Workspace);
|
ProjectManager = new TestProjectSnapshotManager(Workspace);
|
||||||
EngineFactory = new DefaultProjectEngineFactoryService(ProjectManager, FallbackFactory, CustomFactories);
|
EngineFactory = new DefaultProjectSnapshotProjectEngineFactory(FallbackFactory, CustomFactories);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ErrorReporter ErrorReporter { get; }
|
private ErrorReporter ErrorReporter { get; }
|
||||||
|
|
||||||
private RazorProjectEngineFactoryService EngineFactory { get; }
|
private ProjectSnapshotProjectEngineFactory EngineFactory { get; }
|
||||||
|
|
||||||
private Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[] CustomFactories { get; }
|
private Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[] CustomFactories { get; }
|
||||||
|
|
||||||
|
|
@ -72,7 +72,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
// Arrange
|
// Arrange
|
||||||
ProjectManager.HostProjectAdded(HostProject_For_2_0);
|
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);
|
var resolver = new TestTagHelperResolver(EngineFactory, ErrorReporter, Workspace);
|
||||||
|
|
||||||
|
|
@ -89,7 +89,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
ProjectManager.HostProjectAdded(HostProject_For_2_0);
|
ProjectManager.HostProjectAdded(HostProject_For_2_0);
|
||||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||||
|
|
||||||
var project = ProjectManager.GetProjectWithFilePath("Test.csproj");
|
var project = ProjectManager.GetLoadedProject("Test.csproj");
|
||||||
|
|
||||||
var resolver = new TestTagHelperResolver(EngineFactory, ErrorReporter, Workspace)
|
var resolver = new TestTagHelperResolver(EngineFactory, ErrorReporter, Workspace)
|
||||||
{
|
{
|
||||||
|
|
@ -105,7 +105,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
var result = await resolver.GetTagHelpersAsync(project);
|
var result = await resolver.GetTagHelpersAsync(project);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Same(TagHelperResolutionResult.Empty, result);
|
Assert.Same(TagHelperResolutionResult.Empty, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -115,7 +115,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
ProjectManager.HostProjectAdded(HostProject_For_NonSerializableConfiguration);
|
ProjectManager.HostProjectAdded(HostProject_For_NonSerializableConfiguration);
|
||||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||||
|
|
||||||
var project = ProjectManager.GetProjectWithFilePath("Test.csproj");
|
var project = ProjectManager.GetLoadedProject("Test.csproj");
|
||||||
|
|
||||||
var resolver = new TestTagHelperResolver(EngineFactory, ErrorReporter, Workspace)
|
var resolver = new TestTagHelperResolver(EngineFactory, ErrorReporter, Workspace)
|
||||||
{
|
{
|
||||||
|
|
@ -136,8 +136,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
|
|
||||||
private class TestTagHelperResolver : OOPTagHelperResolver
|
private class TestTagHelperResolver : OOPTagHelperResolver
|
||||||
{
|
{
|
||||||
public TestTagHelperResolver(RazorProjectEngineFactoryService engineFactory, ErrorReporter errorReporter, Workspace workspace)
|
public TestTagHelperResolver(ProjectSnapshotProjectEngineFactory factory, ErrorReporter errorReporter, Workspace workspace)
|
||||||
: base(engineFactory, errorReporter, workspace)
|
: base(factory, errorReporter, workspace)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -163,15 +163,10 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
: base(
|
: base(
|
||||||
Mock.Of<ForegroundDispatcher>(),
|
Mock.Of<ForegroundDispatcher>(),
|
||||||
Mock.Of<ErrorReporter>(),
|
Mock.Of<ErrorReporter>(),
|
||||||
Mock.Of<ProjectSnapshotWorker>(),
|
|
||||||
Enumerable.Empty<ProjectSnapshotChangeTrigger>(),
|
Enumerable.Empty<ProjectSnapshotChangeTrigger>(),
|
||||||
workspace)
|
workspace)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void NotifyBackgroundWorker(ProjectSnapshotUpdateContext context)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
|
using Microsoft.CodeAnalysis.Host;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -12,9 +16,31 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
{
|
{
|
||||||
public DefaultProjectSnapshotManagerTest()
|
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);
|
ProjectManager = new TestProjectSnapshotManager(Dispatcher, Enumerable.Empty<ProjectSnapshotChangeTrigger>(), Workspace);
|
||||||
|
|
||||||
var projectId = ProjectId.CreateNewId("Test");
|
var projectId = ProjectId.CreateNewId("Test");
|
||||||
|
|
@ -24,7 +50,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
"Test",
|
"Test",
|
||||||
"Test",
|
"Test",
|
||||||
LanguageNames.CSharp,
|
LanguageNames.CSharp,
|
||||||
"Test.csproj"));
|
"c:\\MyProject\\Test.csproj"));
|
||||||
WorkspaceProject = solution.GetProject(projectId);
|
WorkspaceProject = solution.GetProject(projectId);
|
||||||
|
|
||||||
var vbProjectId = ProjectId.CreateNewId("VB");
|
var vbProjectId = ProjectId.CreateNewId("VB");
|
||||||
|
|
@ -54,12 +80,19 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
"Test (Different TFM)",
|
"Test (Different TFM)",
|
||||||
"Test",
|
"Test",
|
||||||
LanguageNames.CSharp,
|
LanguageNames.CSharp,
|
||||||
"Test.csproj"));
|
"c:\\MyProject\\Test.csproj"));
|
||||||
WorkspaceProjectWithDifferentTfm = solution.GetProject(projectIdWithDifferentTfm);
|
WorkspaceProjectWithDifferentTfm = solution.GetProject(projectIdWithDifferentTfm);
|
||||||
|
|
||||||
|
SomeTagHelpers = TagHelperResolver.TagHelpers;
|
||||||
|
SomeTagHelpers.Add(TagHelperDescriptorBuilder.Create("Test1", "TestAssembly").Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private HostDocument[] Documents { get; }
|
||||||
|
|
||||||
private HostProject HostProject { get; }
|
private HostProject HostProject { get; }
|
||||||
|
|
||||||
|
private HostProject HostProjectWithConfigurationChange { get; }
|
||||||
|
|
||||||
private Project WorkspaceProject { get; }
|
private Project WorkspaceProject { get; }
|
||||||
|
|
||||||
private Project WorkspaceProjectWithDifferentTfm { get; }
|
private Project WorkspaceProjectWithDifferentTfm { get; }
|
||||||
|
|
@ -68,30 +101,203 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
||||||
private Project VBWorkspaceProject { get; }
|
private Project VBWorkspaceProject { get; }
|
||||||
|
|
||||||
|
private TestTagHelperResolver TagHelperResolver { get; }
|
||||||
|
|
||||||
private TestProjectSnapshotManager ProjectManager { get; }
|
private TestProjectSnapshotManager ProjectManager { get; }
|
||||||
|
|
||||||
|
private HostServices HostServices { get; }
|
||||||
|
|
||||||
private Workspace Workspace { get; }
|
private Workspace Workspace { get; }
|
||||||
|
|
||||||
|
private IList<TagHelperDescriptor> SomeTagHelpers { get; }
|
||||||
|
|
||||||
[ForegroundFact]
|
[ForegroundFact]
|
||||||
public void HostProjectBuildComplete_FindsChangedWorkspaceProject_AndStartsBackgroundWorker()
|
public void DocumentAdded_AddsDocument()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
Assert.True(Workspace.TryApplyChanges(WorkspaceProject.Solution));
|
|
||||||
ProjectManager.HostProjectAdded(HostProject);
|
ProjectManager.HostProjectAdded(HostProject);
|
||||||
var project = WorkspaceProject.WithAssemblyName("Test1"); // Simulate a project change
|
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||||
ProjectManager.WorkspaceProjectAdded(project);
|
|
||||||
ProjectManager.Reset();
|
ProjectManager.Reset();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
ProjectManager.HostProjectBuildComplete(HostProject);
|
ProjectManager.DocumentAdded(HostProject, Documents[0]);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||||
Assert.True(snapshot.IsDirty);
|
Assert.Collection(snapshot.DocumentFilePaths, d => Assert.Equal(Documents[0].FilePath, d));
|
||||||
Assert.True(snapshot.IsInitialized);
|
|
||||||
|
|
||||||
Assert.False(ProjectManager.ListenersNotified);
|
Assert.Equal(ProjectChangeKind.DocumentsChanged, ProjectManager.ListenersNotifiedOf);
|
||||||
Assert.True(ProjectManager.WorkerStarted);
|
}
|
||||||
|
|
||||||
|
[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]
|
[ForegroundFact]
|
||||||
|
|
@ -104,15 +310,13 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||||
Assert.True(snapshot.IsDirty);
|
|
||||||
Assert.False(snapshot.IsInitialized);
|
Assert.False(snapshot.IsInitialized);
|
||||||
|
|
||||||
Assert.True(ProjectManager.ListenersNotified);
|
Assert.Equal(ProjectChangeKind.ProjectAdded, ProjectManager.ListenersNotifiedOf);
|
||||||
Assert.False(ProjectManager.WorkerStarted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[ForegroundFact]
|
[ForegroundFact]
|
||||||
public void HostProjectAdded_FindsWorkspaceProject_NotifiesListeners_AndStartsBackgroundWorker()
|
public void HostProjectAdded_FindsWorkspaceProject_NotifiesListeners()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
Assert.True(Workspace.TryApplyChanges(WorkspaceProject.Solution));
|
Assert.True(Workspace.TryApplyChanges(WorkspaceProject.Solution));
|
||||||
|
|
@ -122,60 +326,85 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||||
Assert.True(snapshot.IsDirty);
|
|
||||||
Assert.True(snapshot.IsInitialized);
|
Assert.True(snapshot.IsInitialized);
|
||||||
|
|
||||||
Assert.True(ProjectManager.ListenersNotified);
|
Assert.Equal(ProjectChangeKind.ProjectAdded, ProjectManager.ListenersNotifiedOf);
|
||||||
Assert.True(ProjectManager.WorkerStarted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[ForegroundFact]
|
[ForegroundFact]
|
||||||
public void HostProjectChanged_WithoutWorkspaceProject_NotifiesListeners_AndDoesNotStartBackgroundWorker()
|
public void HostProjectChanged_ConfigurationChange_WithoutWorkspaceProject_NotifiesListeners()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
ProjectManager.HostProjectAdded(HostProject);
|
ProjectManager.HostProjectAdded(HostProject);
|
||||||
ProjectManager.Reset();
|
ProjectManager.Reset();
|
||||||
|
|
||||||
var project = new HostProject(HostProject.FilePath, FallbackRazorConfiguration.MVC_1_0); // Simulate a project change
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
ProjectManager.HostProjectChanged(project);
|
ProjectManager.HostProjectChanged(HostProjectWithConfigurationChange);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
var snapshot = ProjectManager.GetSnapshot(HostProjectWithConfigurationChange);
|
||||||
Assert.True(snapshot.IsDirty);
|
|
||||||
Assert.False(snapshot.IsInitialized);
|
Assert.False(snapshot.IsInitialized);
|
||||||
|
|
||||||
Assert.True(ProjectManager.ListenersNotified);
|
Assert.Equal(ProjectChangeKind.ProjectChanged, ProjectManager.ListenersNotifiedOf);
|
||||||
Assert.False(ProjectManager.WorkerStarted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[ForegroundFact]
|
[ForegroundFact]
|
||||||
public void HostProjectChanged_WithWorkspaceProject_RetainsComputedState_NotifiesListeners_AndStartsBackgroundWorker()
|
public void HostProjectChanged_ConfigurationChange_WithWorkspaceProject_NotifiesListeners()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
ProjectManager.HostProjectAdded(HostProject);
|
ProjectManager.HostProjectAdded(HostProject);
|
||||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||||
ProjectManager.Reset();
|
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
|
// Act
|
||||||
ProjectManager.HostProjectChanged(project);
|
ProjectManager.HostProjectChanged(HostProjectWithConfigurationChange);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
snapshot = ProjectManager.GetSnapshot(project);
|
var snapshot = ProjectManager.GetSnapshot(HostProjectWithConfigurationChange);
|
||||||
Assert.True(snapshot.IsDirty);
|
|
||||||
Assert.True(snapshot.IsInitialized);
|
Assert.True(snapshot.IsInitialized);
|
||||||
|
|
||||||
Assert.True(ProjectManager.ListenersNotified);
|
Assert.Equal(ProjectChangeKind.ProjectChanged, ProjectManager.ListenersNotifiedOf);
|
||||||
Assert.True(ProjectManager.WorkerStarted);
|
}
|
||||||
|
|
||||||
|
[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]
|
[ForegroundFact]
|
||||||
|
|
@ -189,8 +418,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Empty(ProjectManager.Projects);
|
Assert.Empty(ProjectManager.Projects);
|
||||||
|
|
||||||
Assert.False(ProjectManager.ListenersNotified);
|
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||||
Assert.False(ProjectManager.WorkerStarted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[ForegroundFact]
|
[ForegroundFact]
|
||||||
|
|
@ -206,277 +434,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Empty(ProjectManager.Projects);
|
Assert.Empty(ProjectManager.Projects);
|
||||||
|
|
||||||
Assert.True(ProjectManager.ListenersNotified);
|
Assert.Equal(ProjectChangeKind.ProjectRemoved, ProjectManager.ListenersNotifiedOf);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[ForegroundFact]
|
[ForegroundFact]
|
||||||
|
|
@ -490,8 +448,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Empty(ProjectManager.Projects);
|
Assert.Empty(ProjectManager.Projects);
|
||||||
|
|
||||||
Assert.False(ProjectManager.ListenersNotified);
|
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||||
Assert.False(ProjectManager.WorkerStarted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[ForegroundFact]
|
[ForegroundFact]
|
||||||
|
|
@ -508,8 +465,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||||
Assert.False(snapshot.IsInitialized);
|
Assert.False(snapshot.IsInitialized);
|
||||||
|
|
||||||
Assert.False(ProjectManager.ListenersNotified);
|
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||||
Assert.False(ProjectManager.WorkerStarted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[ForegroundFact]
|
[ForegroundFact]
|
||||||
|
|
@ -527,8 +483,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||||
Assert.Same(WorkspaceProject, snapshot.WorkspaceProject);
|
Assert.Same(WorkspaceProject, snapshot.WorkspaceProject);
|
||||||
|
|
||||||
Assert.False(ProjectManager.ListenersNotified);
|
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||||
Assert.False(ProjectManager.WorkerStarted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[ForegroundFact]
|
[ForegroundFact]
|
||||||
|
|
@ -545,12 +500,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||||
Assert.False(snapshot.IsInitialized);
|
Assert.False(snapshot.IsInitialized);
|
||||||
|
|
||||||
Assert.False(ProjectManager.ListenersNotified);
|
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||||
Assert.False(ProjectManager.WorkerStarted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[ForegroundFact]
|
[ForegroundFact]
|
||||||
public void WorkspaceProjectAdded_WithHostProject_NotifiesListenters_AndStartsBackgroundWorker()
|
public void WorkspaceProjectAdded_WithHostProject_NotifiesListenters()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
ProjectManager.HostProjectAdded(HostProject);
|
ProjectManager.HostProjectAdded(HostProject);
|
||||||
|
|
@ -561,11 +515,45 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||||
Assert.True(snapshot.IsDirty);
|
|
||||||
Assert.True(snapshot.IsInitialized);
|
Assert.True(snapshot.IsInitialized);
|
||||||
|
|
||||||
Assert.True(ProjectManager.ListenersNotified);
|
Assert.Equal(ProjectChangeKind.ProjectChanged, ProjectManager.ListenersNotifiedOf);
|
||||||
Assert.True(ProjectManager.WorkerStarted);
|
}
|
||||||
|
|
||||||
|
[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]
|
[ForegroundFact]
|
||||||
|
|
@ -583,8 +571,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Empty(ProjectManager.Projects);
|
Assert.Empty(ProjectManager.Projects);
|
||||||
|
|
||||||
Assert.False(ProjectManager.ListenersNotified);
|
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||||
Assert.False(ProjectManager.WorkerStarted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[ForegroundFact]
|
[ForegroundFact]
|
||||||
|
|
@ -604,11 +591,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||||
Assert.False(snapshot.IsInitialized);
|
Assert.False(snapshot.IsInitialized);
|
||||||
|
|
||||||
Assert.False(ProjectManager.ListenersNotified);
|
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||||
Assert.False(ProjectManager.WorkerStarted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[ForegroundFact]
|
[ForegroundFact]
|
||||||
public void WorkspaceProjectChanged_IgnoresProjectWithoutFilePath()
|
public void WorkspaceProjectChanged_IgnoresProjectWithoutFilePath()
|
||||||
{
|
{
|
||||||
|
|
@ -626,8 +611,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||||
Assert.False(snapshot.IsInitialized);
|
Assert.False(snapshot.IsInitialized);
|
||||||
|
|
||||||
Assert.False(ProjectManager.ListenersNotified);
|
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||||
Assert.False(ProjectManager.WorkerStarted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[ForegroundFact]
|
[ForegroundFact]
|
||||||
|
|
@ -645,65 +629,46 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||||
Assert.Same(WorkspaceProject, snapshot.WorkspaceProject);
|
Assert.Same(WorkspaceProject, snapshot.WorkspaceProject);
|
||||||
|
|
||||||
Assert.False(ProjectManager.ListenersNotified);
|
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||||
Assert.False(ProjectManager.WorkerStarted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[ForegroundFact]
|
[ForegroundFact]
|
||||||
public void WorkspaceProjectChanged_MadeDirty_RetainsComputedState_NotifiesListeners_AndStartsBackgroundWorker()
|
public async Task WorkspaceProjectRemoved_DoesNotRemoveProject_RemovesTagHelpers()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
ProjectManager.HostProjectAdded(HostProject);
|
ProjectManager.HostProjectAdded(HostProject);
|
||||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||||
ProjectManager.Reset();
|
ProjectManager.Reset();
|
||||||
|
|
||||||
// Generate the update
|
|
||||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||||
var updateContext = snapshot.CreateUpdateContext();
|
|
||||||
ProjectManager.ProjectUpdated(updateContext);
|
|
||||||
ProjectManager.Reset();
|
|
||||||
|
|
||||||
var project = WorkspaceProject.WithAssemblyName("Test1"); // Simulate a project change
|
// Adding some computed state
|
||||||
|
await snapshot.GetTagHelpersAsync();
|
||||||
// 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();
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
ProjectManager.WorkspaceProjectRemoved(WorkspaceProject);
|
ProjectManager.WorkspaceProjectRemoved(WorkspaceProject);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||||
Assert.True(snapshot.IsDirty);
|
|
||||||
Assert.False(snapshot.IsInitialized);
|
Assert.False(snapshot.IsInitialized);
|
||||||
|
Assert.False(snapshot.TryGetTagHelpers(out var _));
|
||||||
|
|
||||||
Assert.True(ProjectManager.ListenersNotified);
|
Assert.Equal(ProjectChangeKind.ProjectChanged, ProjectManager.ListenersNotifiedOf);
|
||||||
Assert.False(ProjectManager.WorkerStarted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[ForegroundFact]
|
[ForegroundFact]
|
||||||
public void WorkspaceProjectRemoved_WithHostProject_FallsBackToSecondProject()
|
public async Task WorkspaceProjectRemoved_FallsBackToSecondProject()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
ProjectManager.HostProjectAdded(HostProject);
|
ProjectManager.HostProjectAdded(HostProject);
|
||||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||||
ProjectManager.Reset();
|
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
|
// Sets up a solution where the which has WorkspaceProjectWithDifferentTfm but not WorkspaceProject
|
||||||
// This will enable us to fall back and find the WorkspaceProjectWithDifferentTfm
|
// This will enable us to fall back and find the WorkspaceProjectWithDifferentTfm
|
||||||
Assert.True(Workspace.TryApplyChanges(WorkspaceProjectWithDifferentTfm.Solution));
|
Assert.True(Workspace.TryApplyChanges(WorkspaceProjectWithDifferentTfm.Solution));
|
||||||
|
|
@ -712,13 +677,12 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
ProjectManager.WorkspaceProjectRemoved(WorkspaceProject);
|
ProjectManager.WorkspaceProjectRemoved(WorkspaceProject);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||||
Assert.True(snapshot.IsDirty);
|
|
||||||
Assert.True(snapshot.IsInitialized);
|
Assert.True(snapshot.IsInitialized);
|
||||||
Assert.Equal(WorkspaceProjectWithDifferentTfm.Id, snapshot.WorkspaceProject.Id);
|
Assert.Equal(WorkspaceProjectWithDifferentTfm.Id, snapshot.WorkspaceProject.Id);
|
||||||
|
Assert.False(snapshot.TryGetTagHelpers(out var _));
|
||||||
|
|
||||||
Assert.True(ProjectManager.ListenersNotified);
|
Assert.Equal(ProjectChangeKind.ProjectChanged, ProjectManager.ListenersNotifiedOf);
|
||||||
Assert.True(ProjectManager.WorkerStarted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[ForegroundFact]
|
[ForegroundFact]
|
||||||
|
|
@ -736,8 +700,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||||
Assert.Same(WorkspaceProject, snapshot.WorkspaceProject);
|
Assert.Same(WorkspaceProject, snapshot.WorkspaceProject);
|
||||||
|
|
||||||
Assert.False(ProjectManager.ListenersNotified);
|
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||||
Assert.False(ProjectManager.WorkerStarted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[ForegroundFact]
|
[ForegroundFact]
|
||||||
|
|
@ -755,8 +718,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||||
Assert.False(snapshot.IsInitialized);
|
Assert.False(snapshot.IsInitialized);
|
||||||
|
|
||||||
Assert.False(ProjectManager.ListenersNotified);
|
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||||
Assert.False(ProjectManager.WorkerStarted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[ForegroundFact]
|
[ForegroundFact]
|
||||||
|
|
@ -774,8 +736,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||||
Assert.False(snapshot.IsInitialized);
|
Assert.False(snapshot.IsInitialized);
|
||||||
|
|
||||||
Assert.False(ProjectManager.ListenersNotified);
|
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||||
Assert.False(ProjectManager.WorkerStarted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[ForegroundFact]
|
[ForegroundFact]
|
||||||
|
|
@ -789,20 +750,17 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Empty(ProjectManager.Projects);
|
Assert.Empty(ProjectManager.Projects);
|
||||||
|
|
||||||
Assert.False(ProjectManager.ListenersNotified);
|
Assert.Null(ProjectManager.ListenersNotifiedOf);
|
||||||
Assert.False(ProjectManager.WorkerStarted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestProjectSnapshotManager : DefaultProjectSnapshotManager
|
private class TestProjectSnapshotManager : DefaultProjectSnapshotManager
|
||||||
{
|
{
|
||||||
public TestProjectSnapshotManager(ForegroundDispatcher dispatcher, IEnumerable<ProjectSnapshotChangeTrigger> triggers, Workspace workspace)
|
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 ProjectChangeKind? ListenersNotifiedOf { get; private set; }
|
||||||
|
|
||||||
public bool WorkerStarted { get; private set; }
|
|
||||||
|
|
||||||
public DefaultProjectSnapshot GetSnapshot(HostProject hostProject)
|
public DefaultProjectSnapshot GetSnapshot(HostProject hostProject)
|
||||||
{
|
{
|
||||||
|
|
@ -816,21 +774,12 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
ListenersNotified = false;
|
ListenersNotifiedOf = null;
|
||||||
WorkerStarted = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void NotifyListeners(ProjectChangeEventArgs e)
|
protected override void NotifyListeners(ProjectChangeEventArgs e)
|
||||||
{
|
{
|
||||||
ListenersNotified = true;
|
ListenersNotifiedOf = e.Kind;
|
||||||
}
|
|
||||||
|
|
||||||
protected override void NotifyBackgroundWorker(ProjectSnapshotUpdateContext context)
|
|
||||||
{
|
|
||||||
Assert.NotNull(context.HostProject);
|
|
||||||
Assert.NotNull(context.WorkspaceProject);
|
|
||||||
|
|
||||||
WorkerStarted = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,11 @@ using System.Collections.Immutable;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Microsoft.VisualStudio.LanguageServices.Razor;
|
|
||||||
using Microsoft.VisualStudio.ProjectSystem;
|
using Microsoft.VisualStudio.ProjectSystem;
|
||||||
using Microsoft.VisualStudio.ProjectSystem.Properties;
|
using Microsoft.VisualStudio.ProjectSystem.Properties;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
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
|
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
{
|
{
|
||||||
|
|
@ -22,8 +21,21 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
{
|
{
|
||||||
Workspace = new AdhocWorkspace();
|
Workspace = new AdhocWorkspace();
|
||||||
ProjectManager = new TestProjectSnapshotManager(Dispatcher, Workspace);
|
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 TestProjectSnapshotManager ProjectManager { get; }
|
||||||
|
|
||||||
private Workspace Workspace { get; }
|
private Workspace Workspace { get; }
|
||||||
|
|
@ -285,11 +297,13 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
public void TryGetConfiguredExtensionNames_FailsIfNoExtensions()
|
public void TryGetConfiguredExtensionNames_FailsIfNoExtensions()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var extensions = new Dictionary<string, string>().ToImmutableDictionary();
|
var items = new ItemCollection(Rules.RazorConfiguration.SchemaName);
|
||||||
var configurationItem = new ProjectStateItem(Rules.RazorConfiguration.SchemaName, extensions);
|
items.Item("Test");
|
||||||
|
|
||||||
|
var item = items.ToSnapshot().Items.Single();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = DefaultRazorProjectHost.TryGetConfiguredExtensionNames(configurationItem, out var configuredExtensionnames);
|
var result = DefaultRazorProjectHost.TryGetExtensionNames(item, out var configuredExtensionnames);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result);
|
Assert.False(result);
|
||||||
|
|
@ -300,14 +314,14 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
public void TryGetConfiguredExtensionNames_FailsIfEmptyExtensions()
|
public void TryGetConfiguredExtensionNames_FailsIfEmptyExtensions()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var extensions = new Dictionary<string, string>()
|
var items = new ItemCollection(Rules.RazorConfiguration.SchemaName);
|
||||||
{
|
items.Item("Test");
|
||||||
[Rules.RazorConfiguration.ExtensionsProperty] = string.Empty
|
items.Property("Test", Rules.RazorConfiguration.ExtensionsProperty, string.Empty);
|
||||||
}.ToImmutableDictionary();
|
|
||||||
var configurationItem = new ProjectStateItem(Rules.RazorConfiguration.SchemaName, extensions);
|
var item = items.ToSnapshot().Items.Single();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = DefaultRazorProjectHost.TryGetConfiguredExtensionNames(configurationItem, out var configuredExtensionNames);
|
var result = DefaultRazorProjectHost.TryGetExtensionNames(item, out var configuredExtensionNames);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result);
|
Assert.False(result);
|
||||||
|
|
@ -319,14 +333,15 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var expectedExtensionName = "SomeExtensionName";
|
var expectedExtensionName = "SomeExtensionName";
|
||||||
var extensions = new Dictionary<string, string>()
|
|
||||||
{
|
var items = new ItemCollection(Rules.RazorConfiguration.SchemaName);
|
||||||
[Rules.RazorConfiguration.ExtensionsProperty] = expectedExtensionName
|
items.Item("Test");
|
||||||
}.ToImmutableDictionary();
|
items.Property("Test", Rules.RazorConfiguration.ExtensionsProperty, "SomeExtensionName");
|
||||||
var configurationItem = new ProjectStateItem(Rules.RazorConfiguration.SchemaName, extensions);
|
|
||||||
|
var item = items.ToSnapshot().Items.Single();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = DefaultRazorProjectHost.TryGetConfiguredExtensionNames(configurationItem, out var configuredExtensionNames);
|
var result = DefaultRazorProjectHost.TryGetExtensionNames(item, out var configuredExtensionNames);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(result);
|
Assert.True(result);
|
||||||
|
|
@ -338,14 +353,14 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
public void TryGetConfiguredExtensionNames_SucceedsIfMultipleExtensions()
|
public void TryGetConfiguredExtensionNames_SucceedsIfMultipleExtensions()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var extensions = new Dictionary<string, string>()
|
var items = new ItemCollection(Rules.RazorConfiguration.SchemaName);
|
||||||
{
|
items.Item("Test");
|
||||||
[Rules.RazorConfiguration.ExtensionsProperty] = "SomeExtensionName;SomeOtherExtensionName"
|
items.Property("Test", Rules.RazorConfiguration.ExtensionsProperty, "SomeExtensionName;SomeOtherExtensionName");
|
||||||
}.ToImmutableDictionary();
|
|
||||||
var configurationItem = new ProjectStateItem(Rules.RazorConfiguration.SchemaName, extensions);
|
var item = items.ToSnapshot().Items.Single();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = DefaultRazorProjectHost.TryGetConfiguredExtensionNames(configurationItem, out var configuredExtensionNames);
|
var result = DefaultRazorProjectHost.TryGetExtensionNames(item, out var configuredExtensionNames);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(result);
|
Assert.True(result);
|
||||||
|
|
@ -597,7 +612,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
public async Task DefaultRazorProjectHost_ForegroundThread_CreateAndDispose_Succeeds()
|
public async Task DefaultRazorProjectHost_ForegroundThread_CreateAndDispose_Succeeds()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var services = new TestProjectSystemServices("Test.csproj");
|
var services = new TestProjectSystemServices("c:\\MyProject\\Test.csproj");
|
||||||
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
|
|
@ -612,7 +627,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
public async Task DefaultRazorProjectHost_BackgroundThread_CreateAndDispose_Succeeds()
|
public async Task DefaultRazorProjectHost_BackgroundThread_CreateAndDispose_Succeeds()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var services = new TestProjectSystemServices("Test.csproj");
|
var services = new TestProjectSystemServices("c:\\MyProject\\Test.csproj");
|
||||||
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
|
|
@ -623,41 +638,50 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
Assert.Empty(ProjectManager.Projects);
|
Assert.Empty(ProjectManager.Projects);
|
||||||
}
|
}
|
||||||
|
|
||||||
[ForegroundFact]
|
[ForegroundFact] // This can happen if the .xaml files aren't included correctly.
|
||||||
public async Task OnProjectChanged_ReadsProperties_InitializesProject()
|
public async Task DefaultRazorProjectHost_OnProjectChanged_NoRulesDefined()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var changes = new TestProjectChangeDescription[]
|
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 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);
|
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
||||||
|
|
||||||
|
|
@ -669,7 +693,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var snapshot = Assert.Single(ProjectManager.Projects);
|
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(RazorLanguageVersion.Version_2_1, snapshot.Configuration.LanguageVersion);
|
||||||
Assert.Equal("MVC-2.1", snapshot.Configuration.ConfigurationName);
|
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("MVC-2.1", e.ExtensionName),
|
||||||
e => Assert.Equal("Another-Thing", 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());
|
await Task.Run(async () => await host.DisposeAsync());
|
||||||
Assert.Empty(ProjectManager.Projects);
|
Assert.Empty(ProjectManager.Projects);
|
||||||
}
|
}
|
||||||
|
|
@ -686,34 +719,24 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
public async Task OnProjectChanged_NoVersionFound_DoesNotIniatializeProject()
|
public async Task OnProjectChanged_NoVersionFound_DoesNotIniatializeProject()
|
||||||
{
|
{
|
||||||
// Arrange
|
// 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[]
|
var changes = new TestProjectChangeDescription[]
|
||||||
{
|
{
|
||||||
new TestProjectChangeDescription()
|
RazorGeneralProperties.ToChange(),
|
||||||
{
|
ConfigurationItems.ToChange(),
|
||||||
RuleName = Rules.RazorGeneral.SchemaName,
|
ExtensionItems.ToChange(),
|
||||||
After = TestProjectRuleSnapshot.CreateProperties(Rules.RazorGeneral.SchemaName, new Dictionary<string, string>()
|
DocumentItems.ToChange(),
|
||||||
{
|
|
||||||
{ 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>>()
|
|
||||||
{
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var services = new TestProjectSystemServices("Test.csproj");
|
var services = new TestProjectSystemServices("c:\\MyProject\\Test.csproj");
|
||||||
|
|
||||||
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
||||||
|
|
||||||
|
|
@ -734,37 +757,27 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
public async Task OnProjectChanged_UpdateProject_Succeeds()
|
public async Task OnProjectChanged_UpdateProject_Succeeds()
|
||||||
{
|
{
|
||||||
// Arrange
|
// 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[]
|
var changes = new TestProjectChangeDescription[]
|
||||||
{
|
{
|
||||||
new TestProjectChangeDescription()
|
RazorGeneralProperties.ToChange(),
|
||||||
{
|
ConfigurationItems.ToChange(),
|
||||||
RuleName = Rules.RazorGeneral.SchemaName,
|
ExtensionItems.ToChange(),
|
||||||
After = TestProjectRuleSnapshot.CreateProperties(Rules.RazorGeneral.SchemaName, new Dictionary<string, string>()
|
DocumentItems.ToChange(),
|
||||||
{
|
|
||||||
{ 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 services = new TestProjectSystemServices("c:\\MyProject\\Test.csproj");
|
||||||
|
|
||||||
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
||||||
|
|
||||||
|
|
@ -776,7 +789,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
||||||
// Assert - 1
|
// Assert - 1
|
||||||
var snapshot = Assert.Single(ProjectManager.Projects);
|
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(RazorLanguageVersion.Version_2_1, snapshot.Configuration.LanguageVersion);
|
||||||
Assert.Equal("MVC-2.1", snapshot.Configuration.ConfigurationName);
|
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("MVC-2.1", e.ExtensionName),
|
||||||
e => Assert.Equal("Another-Thing", 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
|
// Act - 2
|
||||||
changes[0].After.SetProperty(Rules.RazorGeneral.RazorLangVersionProperty, "2.0");
|
RazorGeneralProperties.Property(Rules.RazorGeneral.RazorLangVersionProperty, "2.0");
|
||||||
changes[0].After.SetProperty(Rules.RazorGeneral.RazorDefaultConfigurationProperty, "MVC-2.0");
|
RazorGeneralProperties.Property(Rules.RazorGeneral.RazorDefaultConfigurationProperty, "MVC-2.0");
|
||||||
changes[1].After.SetItem("MVC-2.0", new Dictionary<string, string>() { { "Extensions", "MVC-2.0;Another-Thing" }, });
|
ConfigurationItems.RemoveItem("MVC-2.1");
|
||||||
changes[2].After.SetItem("MVC-2.0", new Dictionary<string, string>());
|
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)));
|
await Task.Run(async () => await host.OnProjectChanged(services.CreateUpdate(changes)));
|
||||||
|
|
||||||
// Assert - 2
|
// Assert - 2
|
||||||
snapshot = Assert.Single(ProjectManager.Projects);
|
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(RazorLanguageVersion.Version_2_0, snapshot.Configuration.LanguageVersion);
|
||||||
Assert.Equal("MVC-2.0", snapshot.Configuration.ConfigurationName);
|
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("Another-Thing", e.ExtensionName),
|
||||||
e => Assert.Equal("MVC-2.0", 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());
|
await Task.Run(async () => await host.DisposeAsync());
|
||||||
Assert.Empty(ProjectManager.Projects);
|
Assert.Empty(ProjectManager.Projects);
|
||||||
}
|
}
|
||||||
|
|
@ -812,37 +862,27 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
public async Task OnProjectChanged_VersionRemoved_DeinitializesProject()
|
public async Task OnProjectChanged_VersionRemoved_DeinitializesProject()
|
||||||
{
|
{
|
||||||
// Arrange
|
// 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[]
|
var changes = new TestProjectChangeDescription[]
|
||||||
{
|
{
|
||||||
new TestProjectChangeDescription()
|
RazorGeneralProperties.ToChange(),
|
||||||
{
|
ConfigurationItems.ToChange(),
|
||||||
RuleName = Rules.RazorGeneral.SchemaName,
|
ExtensionItems.ToChange(),
|
||||||
After = TestProjectRuleSnapshot.CreateProperties(Rules.RazorGeneral.SchemaName, new Dictionary<string, string>()
|
DocumentItems.ToChange(),
|
||||||
{
|
|
||||||
{ 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 services = new TestProjectSystemServices("c:\\MyProject\\Test.csproj");
|
||||||
|
|
||||||
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
||||||
|
|
||||||
|
|
@ -854,7 +894,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
||||||
// Assert - 1
|
// Assert - 1
|
||||||
var snapshot = Assert.Single(ProjectManager.Projects);
|
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(RazorLanguageVersion.Version_2_1, snapshot.Configuration.LanguageVersion);
|
||||||
Assert.Equal("MVC-2.1", snapshot.Configuration.ConfigurationName);
|
Assert.Equal("MVC-2.1", snapshot.Configuration.ConfigurationName);
|
||||||
|
|
@ -864,8 +904,16 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
e => Assert.Equal("Another-Thing", e.ExtensionName));
|
e => Assert.Equal("Another-Thing", e.ExtensionName));
|
||||||
|
|
||||||
// Act - 2
|
// Act - 2
|
||||||
changes[0].After.SetProperty(Rules.RazorGeneral.RazorLangVersionProperty, "");
|
RazorGeneralProperties.Property(Rules.RazorGeneral.RazorLangVersionProperty, "");
|
||||||
changes[0].After.SetProperty(Rules.RazorGeneral.RazorDefaultConfigurationProperty, "");
|
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)));
|
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()
|
public async Task OnProjectChanged_AfterDispose_IgnoresUpdate()
|
||||||
{
|
{
|
||||||
// Arrange
|
// 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[]
|
var changes = new TestProjectChangeDescription[]
|
||||||
{
|
{
|
||||||
new TestProjectChangeDescription()
|
RazorGeneralProperties.ToChange(),
|
||||||
{
|
ConfigurationItems.ToChange(),
|
||||||
RuleName = Rules.RazorGeneral.SchemaName,
|
ExtensionItems.ToChange(),
|
||||||
After = TestProjectRuleSnapshot.CreateProperties(Rules.RazorGeneral.SchemaName, new Dictionary<string, string>()
|
DocumentItems.ToChange(),
|
||||||
{
|
|
||||||
{ 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 services = new TestProjectSystemServices("c:\\MyProject\\Test.csproj");
|
||||||
|
|
||||||
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
||||||
|
|
||||||
|
|
@ -922,7 +960,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
||||||
// Assert - 1
|
// Assert - 1
|
||||||
var snapshot = Assert.Single(ProjectManager.Projects);
|
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(RazorLanguageVersion.Version_2_1, snapshot.Configuration.LanguageVersion);
|
||||||
Assert.Equal("MVC-2.1", snapshot.Configuration.ConfigurationName);
|
Assert.Equal("MVC-2.1", snapshot.Configuration.ConfigurationName);
|
||||||
|
|
@ -938,9 +976,17 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
Assert.Empty(ProjectManager.Projects);
|
Assert.Empty(ProjectManager.Projects);
|
||||||
|
|
||||||
// Act - 3
|
// Act - 3
|
||||||
changes[0].After.SetProperty(Rules.RazorGeneral.RazorLangVersionProperty, "2.0");
|
RazorGeneralProperties.Property(Rules.RazorGeneral.RazorLangVersionProperty, "2.0");
|
||||||
changes[0].After.SetProperty(Rules.RazorGeneral.RazorDefaultConfigurationProperty, "MVC-2.0");
|
RazorGeneralProperties.Property(Rules.RazorGeneral.RazorDefaultConfigurationProperty, "MVC-2.0");
|
||||||
changes[1].After.SetItem("MVC-2.0", new Dictionary<string, string>() { { "Extensions", "MVC-2.0;Another-Thing" }, });
|
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)));
|
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()
|
public async Task OnProjectRenamed_RemovesHostProject_CopiesConfiguration()
|
||||||
{
|
{
|
||||||
// Arrange
|
// 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[]
|
var changes = new TestProjectChangeDescription[]
|
||||||
{
|
{
|
||||||
new TestProjectChangeDescription()
|
RazorGeneralProperties.ToChange(),
|
||||||
{
|
ConfigurationItems.ToChange(),
|
||||||
RuleName = Rules.RazorGeneral.SchemaName,
|
ExtensionItems.ToChange(),
|
||||||
After = TestProjectRuleSnapshot.CreateProperties(Rules.RazorGeneral.SchemaName, new Dictionary<string, string>()
|
DocumentItems.ToChange(),
|
||||||
{
|
|
||||||
{ 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 services = new TestProjectSystemServices("c:\\MyProject\\Test.csproj");
|
||||||
|
|
||||||
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
||||||
|
|
||||||
|
|
@ -994,16 +1030,16 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
||||||
// Assert - 1
|
// Assert - 1
|
||||||
var snapshot = Assert.Single(ProjectManager.Projects);
|
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);
|
Assert.Same("MVC-2.1", snapshot.Configuration.ConfigurationName);
|
||||||
|
|
||||||
// Act - 2
|
// Act - 2
|
||||||
services.UnconfiguredProject.FullPath = "Test2.csproj";
|
services.UnconfiguredProject.FullPath = "c:\\AnotherProject\\Test2.csproj";
|
||||||
await Task.Run(async () => await host.OnProjectRenamingAsync());
|
await Task.Run(async () => await host.OnProjectRenamingAsync());
|
||||||
|
|
||||||
// Assert - 1
|
// Assert - 1
|
||||||
snapshot = Assert.Single(ProjectManager.Projects);
|
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);
|
Assert.Same("MVC-2.1", snapshot.Configuration.ConfigurationName);
|
||||||
|
|
||||||
await Task.Run(async () => await host.DisposeAsync());
|
await Task.Run(async () => await host.DisposeAsync());
|
||||||
|
|
@ -1012,12 +1048,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
|
|
||||||
private class TestProjectSnapshotManager : DefaultProjectSnapshotManager
|
private class TestProjectSnapshotManager : DefaultProjectSnapshotManager
|
||||||
{
|
{
|
||||||
public TestProjectSnapshotManager(ForegroundDispatcher dispatcher, Workspace workspace)
|
public TestProjectSnapshotManager(ForegroundDispatcher dispatcher, Workspace workspace)
|
||||||
: base(dispatcher, Mock.Of<ErrorReporter>(), Mock.Of<ProjectSnapshotWorker>(), Array.Empty<ProjectSnapshotChangeTrigger>(), workspace)
|
: base(dispatcher, Mock.Of<ErrorReporter>(), Array.Empty<ProjectSnapshotChangeTrigger>(), workspace)
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void NotifyBackgroundWorker(ProjectSnapshotUpdateContext context)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.LanguageServices.Razor;
|
|
||||||
using Microsoft.VisualStudio.ProjectSystem;
|
using Microsoft.VisualStudio.ProjectSystem;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -17,8 +16,12 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
{
|
{
|
||||||
Workspace = new AdhocWorkspace();
|
Workspace = new AdhocWorkspace();
|
||||||
ProjectManager = new TestProjectSnapshotManager(Dispatcher, Workspace);
|
ProjectManager = new TestProjectSnapshotManager(Dispatcher, Workspace);
|
||||||
|
|
||||||
|
ReferenceItems = new ItemCollection(ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ItemCollection ReferenceItems { get; }
|
||||||
|
|
||||||
private TestProjectSnapshotManager ProjectManager { get; }
|
private TestProjectSnapshotManager ProjectManager { get; }
|
||||||
|
|
||||||
private Workspace Workspace { get; }
|
private Workspace Workspace { get; }
|
||||||
|
|
@ -53,20 +56,37 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
Assert.Empty(ProjectManager.Projects);
|
Assert.Empty(ProjectManager.Projects);
|
||||||
}
|
}
|
||||||
|
|
||||||
[ForegroundFact]
|
[ForegroundFact] // This can happen if the .xaml files aren't included correctly.
|
||||||
public async Task OnProjectChanged_ReadsProperties_InitializesProject()
|
public async Task OnProjectChanged_NoRulesDefined()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var changes = new TestProjectChangeDescription[]
|
var changes = new TestProjectChangeDescription[]
|
||||||
{
|
{
|
||||||
new TestProjectChangeDescription()
|
};
|
||||||
{
|
|
||||||
RuleName = ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName,
|
var services = new TestProjectSystemServices("Test.csproj");
|
||||||
After = TestProjectRuleSnapshot.CreateItems(ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
var host = new TestFallbackRazorProjectHost(services, Workspace, ProjectManager)
|
||||||
{
|
{
|
||||||
{ "c:\\nuget\\Microsoft.AspNetCore.Mvc.razor.dll", new Dictionary<string, string>() },
|
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");
|
var services = new TestProjectSystemServices("Test.csproj");
|
||||||
|
|
@ -97,14 +117,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
// Arrange
|
// Arrange
|
||||||
var changes = new TestProjectChangeDescription[]
|
var changes = new TestProjectChangeDescription[]
|
||||||
{
|
{
|
||||||
new TestProjectChangeDescription()
|
ReferenceItems.ToChange(),
|
||||||
{
|
|
||||||
RuleName = ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName,
|
|
||||||
After = TestProjectRuleSnapshot.CreateItems(ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
|
||||||
{
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
|
|
||||||
};
|
};
|
||||||
var services = new TestProjectSystemServices("Test.csproj");
|
var services = new TestProjectSystemServices("Test.csproj");
|
||||||
|
|
||||||
|
|
@ -127,16 +140,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
public async Task OnProjectChanged_AssemblyFoundButCannotReadVersion_DoesNotIniatializeProject()
|
public async Task OnProjectChanged_AssemblyFoundButCannotReadVersion_DoesNotIniatializeProject()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
ReferenceItems.Item("c:\\nuget\\Microsoft.AspNetCore.Mvc.razor.dll");
|
||||||
|
|
||||||
var changes = new TestProjectChangeDescription[]
|
var changes = new TestProjectChangeDescription[]
|
||||||
{
|
{
|
||||||
new TestProjectChangeDescription()
|
ReferenceItems.ToChange(),
|
||||||
{
|
|
||||||
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 services = new TestProjectSystemServices("Test.csproj");
|
||||||
|
|
@ -160,16 +168,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
public async Task OnProjectChanged_UpdateProject_Succeeds()
|
public async Task OnProjectChanged_UpdateProject_Succeeds()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
ReferenceItems.Item("c:\\nuget\\Microsoft.AspNetCore.Mvc.razor.dll");
|
||||||
|
|
||||||
var changes = new TestProjectChangeDescription[]
|
var changes = new TestProjectChangeDescription[]
|
||||||
{
|
{
|
||||||
new TestProjectChangeDescription()
|
ReferenceItems.ToChange(),
|
||||||
{
|
|
||||||
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 services = new TestProjectSystemServices("Test.csproj");
|
||||||
|
|
@ -207,16 +210,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
public async Task OnProjectChanged_VersionRemoved_DeinitializesProject()
|
public async Task OnProjectChanged_VersionRemoved_DeinitializesProject()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
ReferenceItems.Item("c:\\nuget\\Microsoft.AspNetCore.Mvc.razor.dll");
|
||||||
|
|
||||||
var changes = new TestProjectChangeDescription[]
|
var changes = new TestProjectChangeDescription[]
|
||||||
{
|
{
|
||||||
new TestProjectChangeDescription()
|
ReferenceItems.ToChange(),
|
||||||
{
|
|
||||||
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 services = new TestProjectSystemServices("Test.csproj");
|
||||||
|
|
@ -252,16 +250,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
public async Task OnProjectChanged_AfterDispose_IgnoresUpdate()
|
public async Task OnProjectChanged_AfterDispose_IgnoresUpdate()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
ReferenceItems.Item("c:\\nuget\\Microsoft.AspNetCore.Mvc.razor.dll");
|
||||||
|
|
||||||
var changes = new TestProjectChangeDescription[]
|
var changes = new TestProjectChangeDescription[]
|
||||||
{
|
{
|
||||||
new TestProjectChangeDescription()
|
ReferenceItems.ToChange(),
|
||||||
{
|
|
||||||
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 services = new TestProjectSystemServices("Test.csproj");
|
||||||
|
|
@ -300,16 +293,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
public async Task OnProjectRenamed_RemovesHostProject_CopiesConfiguration()
|
public async Task OnProjectRenamed_RemovesHostProject_CopiesConfiguration()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
ReferenceItems.Item("c:\\nuget\\Microsoft.AspNetCore.Mvc.razor.dll");
|
||||||
|
|
||||||
var changes = new TestProjectChangeDescription[]
|
var changes = new TestProjectChangeDescription[]
|
||||||
{
|
{
|
||||||
new TestProjectChangeDescription()
|
ReferenceItems.ToChange(),
|
||||||
{
|
|
||||||
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 services = new TestProjectSystemServices("Test.csproj");
|
||||||
|
|
@ -361,11 +349,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
private class TestProjectSnapshotManager : DefaultProjectSnapshotManager
|
private class TestProjectSnapshotManager : DefaultProjectSnapshotManager
|
||||||
{
|
{
|
||||||
public TestProjectSnapshotManager(ForegroundDispatcher dispatcher, Workspace workspace)
|
public TestProjectSnapshotManager(ForegroundDispatcher dispatcher, Workspace workspace)
|
||||||
: base(dispatcher, Mock.Of<ErrorReporter>(), Mock.Of<ProjectSnapshotWorker>(), Array.Empty<ProjectSnapshotChangeTrigger>(), workspace)
|
: base(dispatcher, Mock.Of<ErrorReporter>(), Array.Empty<ProjectSnapshotChangeTrigger>(), workspace)
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void NotifyBackgroundWorker(ProjectSnapshotUpdateContext context)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.
|
// 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
|
// 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;
|
using Microsoft.VisualStudio.ProjectSystem.Properties;
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.ProjectSystem
|
namespace Microsoft.VisualStudio.ProjectSystem
|
||||||
{
|
{
|
||||||
internal class TestProjectChangeDescription : IProjectChangeDescription
|
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;
|
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 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; }
|
public IImmutableDictionary<NamedIdentity, IComparable> DataSourceVersions { get; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||||
{
|
{
|
||||||
return new ProjectVersionedValue<IProjectSubscriptionUpdate>(
|
return new ProjectVersionedValue<IProjectSubscriptionUpdate>(
|
||||||
value: new ProjectSubscriptionUpdate(
|
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),
|
projectConfiguration: ActiveConfiguredProject.ProjectConfiguration),
|
||||||
dataSourceVersions: ImmutableDictionary<NamedIdentity, IComparable>.Empty);
|
dataSourceVersions: ImmutableDictionary<NamedIdentity, IComparable>.Empty);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,41 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
{
|
{
|
||||||
public class VsSolutionUpdatesProjectSnapshotChangeTriggerTest
|
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]
|
[Fact]
|
||||||
public void Initialize_AttachesEventSink()
|
public void Initialize_AttachesEventSink()
|
||||||
{
|
{
|
||||||
|
|
@ -38,10 +73,10 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void UpdateProjectCfg_Done_KnownProject_Invokes_ProjectBuildComplete()
|
public void UpdateProjectCfg_Done_KnownProject_Invokes_WorkspaceProjectChanged()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var expectedProjectPath = "Path/To/Project";
|
var expectedProjectPath = SomeProject.FilePath;
|
||||||
|
|
||||||
uint cookie;
|
uint cookie;
|
||||||
var buildManager = new Mock<IVsSolutionBuildManager>(MockBehavior.Strict);
|
var buildManager = new Mock<IVsSolutionBuildManager>(MockBehavior.Strict);
|
||||||
|
|
@ -57,16 +92,19 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
|
|
||||||
var projectSnapshots = new[]
|
var projectSnapshots = new[]
|
||||||
{
|
{
|
||||||
Mock.Of<ProjectSnapshot>(p => p.FilePath == expectedProjectPath && p.HostProject == new HostProject(expectedProjectPath, RazorConfiguration.Default)),
|
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeProject, SomeWorkspaceProject)),
|
||||||
Mock.Of<ProjectSnapshot>(p => p.FilePath == "Test2.csproj" && p.HostProject == new HostProject("Test2.csproj", RazorConfiguration.Default)),
|
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeOtherProject, SomeOtherWorkspaceProject)),
|
||||||
};
|
};
|
||||||
|
|
||||||
var called = false;
|
var called = false;
|
||||||
var projectManager = new Mock<ProjectSnapshotManagerBase>();
|
var projectManager = new Mock<ProjectSnapshotManagerBase>();
|
||||||
projectManager.SetupGet(p => p.Projects).Returns(projectSnapshots);
|
projectManager.SetupGet(p => p.Workspace).Returns(Workspace);
|
||||||
projectManager
|
projectManager
|
||||||
.Setup(p => p.HostProjectBuildComplete(It.IsAny<HostProject>()))
|
.Setup(p => p.GetLoadedProject(expectedProjectPath))
|
||||||
.Callback<HostProject>(c =>
|
.Returns(projectSnapshots[0]);
|
||||||
|
projectManager
|
||||||
|
.Setup(p => p.WorkspaceProjectChanged(It.IsAny<Project>()))
|
||||||
|
.Callback<Project>(c =>
|
||||||
{
|
{
|
||||||
called = true;
|
called = true;
|
||||||
Assert.Equal(expectedProjectPath, c.FilePath);
|
Assert.Equal(expectedProjectPath, c.FilePath);
|
||||||
|
|
@ -83,7 +121,50 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[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
|
// Arrange
|
||||||
var expectedProjectPath = "Path/To/Project";
|
var expectedProjectPath = "Path/To/Project";
|
||||||
|
|
@ -102,15 +183,18 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
|
|
||||||
var projectSnapshots = new[]
|
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, SomeWorkspaceProject)),
|
||||||
Mock.Of<ProjectSnapshot>(p => p.FilePath == "Path/To/DifferenProject" && p.HostProject == new HostProject("Path/To/DifferenProject", RazorConfiguration.Default)),
|
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeOtherProject, SomeOtherWorkspaceProject)),
|
||||||
};
|
};
|
||||||
|
|
||||||
var projectManager = new Mock<ProjectSnapshotManagerBase>();
|
var projectManager = new Mock<ProjectSnapshotManagerBase>();
|
||||||
projectManager.SetupGet(p => p.Projects).Returns(projectSnapshots);
|
projectManager.SetupGet(p => p.Workspace).Returns(Workspace);
|
||||||
projectManager
|
projectManager
|
||||||
.Setup(p => p.HostProjectBuildComplete(It.IsAny<HostProject>()))
|
.Setup(p => p.GetLoadedProject(expectedProjectPath))
|
||||||
.Callback<HostProject>(c =>
|
.Returns((ProjectSnapshot)null);
|
||||||
|
projectManager
|
||||||
|
.Setup(p => p.WorkspaceProjectChanged(It.IsAny<Project>()))
|
||||||
|
.Callback<Project>(c =>
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("This should not be called.");
|
throw new InvalidOperationException("This should not be called.");
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,12 @@
|
||||||
<TargetFramework>net461</TargetFramework>
|
<TargetFramework>net461</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="..\Microsoft.CodeAnalysis.Razor.Workspaces.Test\Shared\**\*.cs">
|
||||||
|
<Link>Shared\%(RecursiveDir)%(FileName)%(Extension)</Link>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
|
<None Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -10,29 +10,71 @@ using MonoDevelop.Projects;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Project = Microsoft.CodeAnalysis.Project;
|
using Project = Microsoft.CodeAnalysis.Project;
|
||||||
|
using Workspace = Microsoft.CodeAnalysis.Workspace;
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor
|
namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor
|
||||||
{
|
{
|
||||||
public class ProjectBuildChangeTriggerTest : ForegroundDispatcherTestBase
|
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]
|
[ForegroundFact]
|
||||||
public void ProjectOperations_EndBuild_Invokes_ProjectBuildComplete()
|
public void ProjectOperations_EndBuild_Invokes_WorkspaceProjectChanged()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var args = new BuildEventArgs(monitor: null, success: true);
|
var expectedProjectPath = SomeProject.FilePath;
|
||||||
var expectedProjectPath = "Path/To/Project";
|
|
||||||
var projectService = CreateProjectService(expectedProjectPath);
|
var projectService = CreateProjectService(expectedProjectPath);
|
||||||
|
|
||||||
|
var args = new BuildEventArgs(monitor: null, success: true);
|
||||||
|
|
||||||
var projectSnapshots = new[]
|
var projectSnapshots = new[]
|
||||||
{
|
{
|
||||||
Mock.Of<ProjectSnapshot>(p => p.FilePath == expectedProjectPath && p.HostProject == new HostProject(expectedProjectPath, RazorConfiguration.Default)),
|
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeProject, SomeWorkspaceProject)),
|
||||||
Mock.Of<ProjectSnapshot>(p => p.FilePath == "Test2.csproj" && p.HostProject == new HostProject("Test2.csproj", RazorConfiguration.Default)),
|
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeOtherProject, SomeOtherWorkspaceProject)),
|
||||||
};
|
};
|
||||||
|
|
||||||
var projectManager = new Mock<ProjectSnapshotManagerBase>(MockBehavior.Strict);
|
var projectManager = new Mock<ProjectSnapshotManagerBase>(MockBehavior.Strict);
|
||||||
projectManager.SetupGet(p => p.Projects).Returns(projectSnapshots);
|
projectManager.SetupGet(p => p.Workspace).Returns(Workspace);
|
||||||
projectManager
|
projectManager
|
||||||
.Setup(p => p.HostProjectBuildComplete(It.IsAny<HostProject>()))
|
.Setup(p => p.GetLoadedProject(SomeProject.FilePath))
|
||||||
.Callback<HostProject>(c => Assert.Equal(expectedProjectPath, c.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);
|
var trigger = new ProjectBuildChangeTrigger(Dispatcher, projectService, projectManager.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -42,21 +84,56 @@ namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor
|
||||||
projectManager.VerifyAll();
|
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]
|
[ForegroundFact]
|
||||||
public void ProjectOperations_EndBuild_UntrackedProject_Noops()
|
public void ProjectOperations_EndBuild_UntrackedProject_Noops()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var args = new BuildEventArgs(monitor: null, success: true);
|
|
||||||
var projectService = CreateProjectService("Path/To/Project");
|
var projectService = CreateProjectService("Path/To/Project");
|
||||||
|
|
||||||
|
var args = new BuildEventArgs(monitor: null, success: true);
|
||||||
var projectSnapshots = new[]
|
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>();
|
var projectManager = new Mock<ProjectSnapshotManagerBase>();
|
||||||
projectManager.SetupGet(p => p.Projects).Returns(projectSnapshots);
|
projectManager.SetupGet(p => p.Workspace).Returns(Workspace);
|
||||||
projectManager
|
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>();
|
.Throws<InvalidOperationException>();
|
||||||
|
|
||||||
var trigger = new ProjectBuildChangeTrigger(Dispatcher, projectService, projectManager.Object);
|
var trigger = new ProjectBuildChangeTrigger(Dispatcher, projectService, projectManager.Object);
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
|
|
@ -100,11 +177,5 @@ namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor
|
||||||
projectService.Setup(p => p.IsSupportedProject(null)).Returns(true);
|
projectService.Setup(p => p.IsSupportedProject(null)).Returns(true);
|
||||||
return projectService.Object;
|
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">
|
<Compile Include="DocumentInfo\RazorDocumentInfoWindowControl.xaml.cs">
|
||||||
<DependentUpon>RazorDocumentInfoWindowControl.xaml</DependentUpon>
|
<DependentUpon>RazorDocumentInfoWindowControl.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="RazorInfo\AssemblyViewModel.cs" />
|
|
||||||
<Compile Include="Behaviors\ItemSelectedBehavior.cs" />
|
<Compile Include="Behaviors\ItemSelectedBehavior.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Properties\BindingRedirectAttributes.cs" />
|
<Compile Include="Properties\BindingRedirectAttributes.cs" />
|
||||||
<Compile Include="RazorInfo\DirectiveViewModel.cs" />
|
<Compile Include="RazorInfo\DirectiveDescriptorViewModel.cs" />
|
||||||
<Compile Include="RazorInfo\DocumentInfoViewModel.cs" />
|
<Compile Include="RazorInfo\DocumentSnapshotViewModel.cs" />
|
||||||
<Compile Include="RazorInfo\DocumentViewModel.cs" />
|
|
||||||
<Compile Include="NotifyPropertyChanged.cs" />
|
<Compile Include="NotifyPropertyChanged.cs" />
|
||||||
<Compile Include="RazorInfo\ProjectSnapshotViewModel.cs" />
|
<Compile Include="RazorInfo\ProjectSnapshotViewModel.cs" />
|
||||||
<Compile Include="RazorInfo\ProjectViewModel.cs" />
|
<Compile Include="RazorInfo\ProjectViewModel.cs" />
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,11 @@ using Microsoft.AspNetCore.Razor.Language;
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||||
{
|
{
|
||||||
public class DirectiveViewModel : NotifyPropertyChanged
|
public class DirectiveDescriptorViewModel : NotifyPropertyChanged
|
||||||
{
|
{
|
||||||
private readonly DirectiveDescriptor _directive;
|
private readonly DirectiveDescriptor _directive;
|
||||||
|
|
||||||
internal DirectiveViewModel(DirectiveDescriptor directive)
|
internal DirectiveDescriptorViewModel(DirectiveDescriptor directive)
|
||||||
{
|
{
|
||||||
_directive = 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||||
|
|
||||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||||
{
|
{
|
||||||
public class AssemblyViewModel : NotifyPropertyChanged
|
public class DocumentSnapshotViewModel : NotifyPropertyChanged
|
||||||
{
|
{
|
||||||
private readonly ProjectExtensibilityAssembly _assembly;
|
internal DocumentSnapshotViewModel(DocumentSnapshot document)
|
||||||
|
|
||||||
internal AssemblyViewModel(ProjectExtensibilityAssembly assembly)
|
|
||||||
{
|
{
|
||||||
_assembly = assembly;
|
Document = document;
|
||||||
|
|
||||||
Name = _assembly.Identity.GetDisplayName();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Name { get; }
|
internal DocumentSnapshot Document { get; }
|
||||||
|
|
||||||
|
public string FilePath => Document.FilePath;
|
||||||
|
|
||||||
|
public string TargetPath => Document.TargetPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||||
{
|
{
|
||||||
public class ProjectInfoViewModel : NotifyPropertyChanged
|
public class ProjectInfoViewModel : NotifyPropertyChanged
|
||||||
{
|
{
|
||||||
private ObservableCollection<DirectiveViewModel> _directives;
|
private ObservableCollection<DirectiveDescriptorViewModel> _directives;
|
||||||
private ObservableCollection<DocumentViewModel> _documents;
|
private ObservableCollection<DocumentSnapshotViewModel> _documents;
|
||||||
private ObservableCollection<TagHelperViewModel> _tagHelpers;
|
private ObservableCollection<TagHelperViewModel> _tagHelpers;
|
||||||
|
private bool _tagHelpersLoading;
|
||||||
|
|
||||||
public ObservableCollection<DirectiveViewModel> Directives
|
public ObservableCollection<DirectiveDescriptorViewModel> Directives
|
||||||
{
|
{
|
||||||
get { return _directives; }
|
get { return _directives; }
|
||||||
set
|
set
|
||||||
|
|
@ -22,7 +24,7 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObservableCollection<DocumentViewModel> Documents
|
public ObservableCollection<DocumentSnapshotViewModel> Documents
|
||||||
{
|
{
|
||||||
get { return _documents; }
|
get { return _documents; }
|
||||||
set
|
set
|
||||||
|
|
@ -41,6 +43,20 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool TagHelpersLoading
|
||||||
|
{
|
||||||
|
get { return _tagHelpersLoading; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_tagHelpersLoading = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
OnPropertyChanged(nameof(TagHelperProgressVisibility));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Visibility TagHelperProgressVisibility => TagHelpersLoading ? Visibility.Visible : Visibility.Hidden;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,10 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||||
|
using System;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||||
|
|
||||||
|
|
@ -16,12 +18,9 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||||
Project = project;
|
Project = project;
|
||||||
|
|
||||||
Id = project.WorkspaceProject?.Id;
|
Id = project.WorkspaceProject?.Id;
|
||||||
Properties = new ObservableCollection<PropertyViewModel>()
|
Properties = new ObservableCollection<PropertyViewModel>();
|
||||||
{
|
|
||||||
new PropertyViewModel("Razor Language Version", project.Configuration?.LanguageVersion.ToString()),
|
InitializeProperties();
|
||||||
new PropertyViewModel("Configuration Name", $"{project.Configuration?.ConfigurationName} ({project.Configuration?.GetType().Name ?? "unknown"})"),
|
|
||||||
new PropertyViewModel("Workspace Project", project.WorkspaceProject?.Name)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal ProjectSnapshot Project { get; }
|
internal ProjectSnapshot Project { get; }
|
||||||
|
|
@ -31,6 +30,26 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||||
public ProjectId Id { get; }
|
public ProjectId Id { get; }
|
||||||
|
|
||||||
public ObservableCollection<PropertyViewModel> Properties { 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
|
#endif
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue