Add and process notifications for Imports (#2656)

This builds support for tracking the effect of changes to imports on
other documents, and completes our model for being able to keep
generated code up to date.
This commit is contained in:
Ryan Nowak 2018-10-19 21:39:43 -07:00 коммит произвёл GitHub
Родитель 333989af7a
Коммит baa71375d0
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
42 изменённых файлов: 1347 добавлений и 434 удалений

Просмотреть файл

@ -132,14 +132,6 @@ namespace Microsoft.AspNetCore.Razor.Performance
private class StaticProjectSnapshotProjectEngineFactory : ProjectSnapshotProjectEngineFactory
{
public override RazorProjectEngine Create(ProjectSnapshot project, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
{
return RazorProjectEngine.Create(project.Configuration, fileSystem, b =>
{
RazorExtensions.Register(b);
});
}
public override IProjectEngineFactory FindFactory(ProjectSnapshot project)
{
throw new NotImplementedException();
@ -149,6 +141,14 @@ namespace Microsoft.AspNetCore.Razor.Performance
{
throw new NotImplementedException();
}
public override RazorProjectEngine Create(RazorConfiguration configuration, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
{
return RazorProjectEngine.Create(configuration, fileSystem, b =>
{
RazorExtensions.Register(b);
});
}
}
}
}

Просмотреть файл

@ -49,6 +49,10 @@ namespace Microsoft.AspNetCore.Razor.Language
var absolutePath = NormalizeAndEnsureValidPath(path);
var file = new FileInfo(absolutePath);
if (!absolutePath.StartsWith(absoluteBasePath))
{
throw new InvalidOperationException($"The file '{file.FullName}' is not a descendent of the base path '{absoluteBasePath}'.");
}
var relativePhysicalPath = file.FullName.Substring(absoluteBasePath.Length + 1); // Include leading separator
var filePath = "/" + relativePhysicalPath.Replace(Path.DirectorySeparatorChar, '/');

Просмотреть файл

@ -32,13 +32,8 @@ namespace Microsoft.CodeAnalysis.Razor
_factories = factories;
}
public override RazorProjectEngine Create(ProjectSnapshot project, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
public override RazorProjectEngine Create(RazorConfiguration configuration, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
{
if (project == null)
{
throw new ArgumentNullException(nameof(project));
}
if (fileSystem == null)
{
throw new ArgumentNullException(nameof(fileSystem));
@ -55,7 +50,7 @@ namespace Microsoft.CodeAnalysis.Razor
//
// 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;
configuration = configuration ?? DefaultConfiguration;
// If there's no factory to handle the configuration then fall back to a very basic configuration.
//

Просмотреть файл

@ -45,7 +45,36 @@ namespace Microsoft.CodeAnalysis.Razor
return Create(project, RazorProjectFileSystem.Create(Path.GetDirectoryName(project.FilePath)), configure);
}
public abstract RazorProjectEngine Create(ProjectSnapshot project, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure);
public 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 Create(project.Configuration, fileSystem, configure);
}
public RazorProjectEngine Create(RazorConfiguration configuration, string directoryPath, Action<RazorProjectEngineBuilder> configure)
{
if (configuration == null)
{
throw new ArgumentNullException(nameof(configuration));
}
if (directoryPath == null)
{
throw new ArgumentNullException(nameof(directoryPath));
}
return Create(configuration, RazorProjectFileSystem.Create(directoryPath), configure);
}
public abstract RazorProjectEngine Create(RazorConfiguration configuration, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure);
}
}

Просмотреть файл

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
@ -58,9 +59,37 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
}
}
public override bool IsImportDocument(DocumentSnapshot document)
{
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
return State.ImportsToRelatedDocuments.ContainsKey(document.TargetPath);
}
public override IEnumerable<DocumentSnapshot> GetRelatedDocuments(DocumentSnapshot document)
{
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
if (State.ImportsToRelatedDocuments.TryGetValue(document.TargetPath, out var relatedDocuments))
{
lock (_lock)
{
return relatedDocuments.Select(GetDocument).ToArray();
}
}
return Array.Empty<DocumentSnapshot>();
}
public override RazorProjectEngine GetProjectEngine()
{
return State.ProjectEngine.GetProjectEngine(this);
return State.ProjectEngine.GetProjectEngine(this.State);
}
public override Task<IReadOnlyList<TagHelperDescriptor>> GetTagHelpersAsync()

Просмотреть файл

@ -90,12 +90,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
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;
projects[i++] = entry.Value.GetSnapshot();
}
return projects;
@ -115,12 +110,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
if (_projects.TryGetValue(filePath, out var entry))
{
if (entry.Snapshot == null)
{
entry.Snapshot = new DefaultProjectSnapshot(entry.State);
}
return entry.Snapshot;
return entry.GetSnapshot();
}
return null;
@ -175,8 +165,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// Document updates can no-op.
if (!object.ReferenceEquals(state, entry.State))
{
_projects[hostProject.FilePath] = new Entry(state);
NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, document.FilePath, ProjectChangeKind.DocumentAdded));
var oldSnapshot = entry.GetSnapshot();
entry = new Entry(state);
_projects[hostProject.FilePath] = entry;
NotifyListeners(new ProjectChangeEventArgs(oldSnapshot, entry.GetSnapshot(), document.FilePath, ProjectChangeKind.DocumentAdded));
}
}
}
@ -201,8 +193,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// Document updates can no-op.
if (!object.ReferenceEquals(state, entry.State))
{
_projects[hostProject.FilePath] = new Entry(state);
NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, document.FilePath, ProjectChangeKind.DocumentRemoved));
var oldSnapshot = entry.GetSnapshot();
entry = new Entry(state);
_projects[hostProject.FilePath] = entry;
NotifyListeners(new ProjectChangeEventArgs(oldSnapshot, entry.GetSnapshot(), document.FilePath, ProjectChangeKind.DocumentRemoved));
}
}
}
@ -229,12 +223,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
entry.State.Documents.TryGetValue(documentFilePath, out var older))
{
ProjectState state;
SourceText olderText;
VersionStamp olderVersion;
var currentText = sourceText;
if (older.TryGetText(out olderText) &&
older.TryGetTextVersion(out olderVersion))
if (older.TryGetText(out var olderText) &&
older.TryGetTextVersion(out var olderVersion))
{
var version = currentText.ContentEquals(olderText) ? olderVersion : olderVersion.GetNewerVersion();
state = entry.State.WithChangedHostDocument(older.HostDocument, currentText, version);
@ -256,8 +248,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// Document updates can no-op.
if (!object.ReferenceEquals(state, entry.State))
{
_projects[projectFilePath] = new Entry(state);
NotifyListeners(new ProjectChangeEventArgs(projectFilePath, documentFilePath, ProjectChangeKind.DocumentChanged));
var oldSnapshot = entry.GetSnapshot();
entry = new Entry(state);
_projects[projectFilePath] = entry;
NotifyListeners(new ProjectChangeEventArgs(oldSnapshot, entry.GetSnapshot(), documentFilePath, ProjectChangeKind.DocumentChanged));
}
}
}
@ -293,8 +287,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// Document updates can no-op.
if (!object.ReferenceEquals(state, entry.State))
{
_projects[projectFilePath] = new Entry(state);
NotifyListeners(new ProjectChangeEventArgs(projectFilePath, documentFilePath, ProjectChangeKind.DocumentChanged));
var oldSnapshot = entry.GetSnapshot();
entry = new Entry(state);
_projects[projectFilePath] = entry;
NotifyListeners(new ProjectChangeEventArgs(oldSnapshot, entry.GetSnapshot(), documentFilePath, ProjectChangeKind.DocumentChanged));
}
}
}
@ -321,12 +317,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
entry.State.Documents.TryGetValue(documentFilePath, out var older))
{
ProjectState state;
SourceText olderText;
VersionStamp olderVersion;
var currentText = sourceText;
if (older.TryGetText(out olderText) &&
older.TryGetTextVersion(out olderVersion))
if (older.TryGetText(out var olderText) &&
older.TryGetTextVersion(out var olderVersion))
{
var version = currentText.ContentEquals(olderText) ? olderVersion : olderVersion.GetNewerVersion();
state = entry.State.WithChangedHostDocument(older.HostDocument, currentText, version);
@ -346,8 +340,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// Document updates can no-op.
if (!object.ReferenceEquals(state, entry.State))
{
_projects[projectFilePath] = new Entry(state);
NotifyListeners(new ProjectChangeEventArgs(projectFilePath, documentFilePath, ProjectChangeKind.DocumentChanged));
var oldSnapshot = entry.GetSnapshot();
entry = new Entry(state);
_projects[projectFilePath] = entry;
NotifyListeners(new ProjectChangeEventArgs(oldSnapshot, entry.GetSnapshot(), documentFilePath, ProjectChangeKind.DocumentChanged));
}
}
}
@ -381,8 +377,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// Document updates can no-op.
if (!object.ReferenceEquals(state, entry.State))
{
_projects[projectFilePath] = new Entry(state);
NotifyListeners(new ProjectChangeEventArgs(projectFilePath, documentFilePath, ProjectChangeKind.DocumentChanged));
var oldSnapshot = entry.GetSnapshot();
entry = new Entry(state);
_projects[projectFilePath] = entry;
NotifyListeners(new ProjectChangeEventArgs(oldSnapshot, entry.GetSnapshot(), documentFilePath, ProjectChangeKind.DocumentChanged));
}
}
}
@ -407,10 +405,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
var workspaceProject = GetWorkspaceProject(hostProject.FilePath);
var state = ProjectState.Create(Workspace.Services, hostProject, workspaceProject);
_projects[hostProject.FilePath] = new Entry(state);
var entry = new Entry(state);
_projects[hostProject.FilePath] = entry;
// We need to notify listeners about every project add.
NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, ProjectChangeKind.ProjectAdded));
NotifyListeners(new ProjectChangeEventArgs(null, entry.GetSnapshot(), ProjectChangeKind.ProjectAdded));
}
public override void HostProjectChanged(HostProject hostProject)
@ -429,9 +428,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// HostProject updates can no-op.
if (!object.ReferenceEquals(state, entry.State))
{
_projects[hostProject.FilePath] = new Entry(state);
NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, ProjectChangeKind.ProjectChanged));
var oldSnapshot = entry.GetSnapshot();
entry = new Entry(state);
_projects[hostProject.FilePath] = entry;
NotifyListeners(new ProjectChangeEventArgs(oldSnapshot, entry.GetSnapshot(), ProjectChangeKind.ProjectChanged));
}
}
}
@ -445,12 +445,12 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
_foregroundDispatcher.AssertForegroundThread();
if (_projects.TryGetValue(hostProject.FilePath, out var snapshot))
if (_projects.TryGetValue(hostProject.FilePath, out var entry))
{
_projects.Remove(hostProject.FilePath);
// We need to notify listeners about every project removal.
NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, ProjectChangeKind.ProjectRemoved));
var oldSnapshot = entry.GetSnapshot();
_projects.Remove(hostProject.FilePath);
NotifyListeners(new ProjectChangeEventArgs(oldSnapshot, null, ProjectChangeKind.ProjectRemoved));
}
}
@ -477,9 +477,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
if (entry.State.WorkspaceProject == null)
{
var state = entry.State.WithWorkspaceProject(workspaceProject);
_projects[workspaceProject.FilePath] = new Entry(state);
NotifyListeners(new ProjectChangeEventArgs(workspaceProject.FilePath, ProjectChangeKind.ProjectChanged));
var oldSnapshot = entry.GetSnapshot();
entry = new Entry(state);
_projects[workspaceProject.FilePath] = entry;
NotifyListeners(new ProjectChangeEventArgs(oldSnapshot, entry.GetSnapshot(), ProjectChangeKind.ProjectChanged));
}
}
}
@ -505,14 +507,15 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
(entry.State.WorkspaceProject == null || entry.State.WorkspaceProject.Version.GetNewerVersion(workspaceProject.Version) == workspaceProject.Version))
{
var state = entry.State.WithWorkspaceProject(workspaceProject);
// WorkspaceProject updates can no-op. This can be the case if a build is triggered, but we've
// already seen the update.
if (!object.ReferenceEquals(state, entry.State))
{
_projects[workspaceProject.FilePath] = new Entry(state);
NotifyListeners(new ProjectChangeEventArgs(workspaceProject.FilePath, ProjectChangeKind.ProjectChanged));
var oldSnapshot = entry.GetSnapshot();
entry = new Entry(state);
_projects[workspaceProject.FilePath] = entry;
NotifyListeners(new ProjectChangeEventArgs(oldSnapshot, entry.GetSnapshot(), ProjectChangeKind.ProjectChanged));
}
}
}
@ -549,17 +552,21 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
// OK there's another WorkspaceProject, use that.
state = entry.State.WithWorkspaceProject(otherWorkspaceProject);
_projects[otherWorkspaceProject.FilePath] = new Entry(state);
NotifyListeners(new ProjectChangeEventArgs(otherWorkspaceProject.FilePath, ProjectChangeKind.ProjectChanged));
var oldSnapshot = entry.GetSnapshot();
entry = new Entry(state);
_projects[otherWorkspaceProject.FilePath] = entry;
NotifyListeners(new ProjectChangeEventArgs(oldSnapshot, entry.GetSnapshot(), ProjectChangeKind.ProjectChanged));
}
else
{
state = entry.State.WithWorkspaceProject(null);
_projects[workspaceProject.FilePath] = new Entry(state);
// Notify listeners of a change because we've removed computed state.
NotifyListeners(new ProjectChangeEventArgs(workspaceProject.FilePath, ProjectChangeKind.ProjectChanged));
state = entry.State.WithWorkspaceProject(null);
var oldSnapshot = entry.GetSnapshot();
entry = new Entry(state);
_projects[workspaceProject.FilePath] = entry;
NotifyListeners(new ProjectChangeEventArgs(oldSnapshot, entry.GetSnapshot(), ProjectChangeKind.ProjectChanged));
}
}
}
@ -601,7 +608,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
throw new ArgumentNullException(nameof(exception));
}
_errorReporter.ReportError(exception, workspaceProject);
}
@ -644,13 +651,18 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
private class Entry
{
public ProjectSnapshot Snapshot;
public ProjectSnapshot SnapshotUnsafe;
public readonly ProjectState State;
public Entry(ProjectState state)
{
State = state;
}
public ProjectSnapshot GetSnapshot()
{
return SnapshotUnsafe ?? (SnapshotUnsafe = new DefaultProjectSnapshot(State));
}
}
}
}

Просмотреть файл

@ -183,6 +183,18 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
return state;
}
public virtual DocumentState WithImportsChange()
{
var state = new DocumentState(Services, HostDocument, _sourceText, _version, _loader);
// The source could not have possibly changed.
state._sourceText = _sourceText;
state._version = _version;
state._loaderTask = _loaderTask;
return state;
}
public virtual DocumentState WithWorkspaceProjectChange()
{
var state = new DocumentState(Services, HostDocument, _sourceText, _version, _loader);

Просмотреть файл

@ -56,6 +56,26 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
return null;
}
public override bool IsImportDocument(DocumentSnapshot document)
{
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
return false;
}
public override IEnumerable<DocumentSnapshot> GetRelatedDocuments(DocumentSnapshot document)
{
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
return Array.Empty<DocumentSnapshot>();
}
public override RazorProjectEngine GetProjectEngine()
{
return _projectEngine.Value;

Просмотреть файл

@ -27,6 +27,10 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces.ProjectSystem
public override DocumentSnapshot GetDocument(string filePath) => throw new NotImplementedException();
public override bool IsImportDocument(DocumentSnapshot document) => throw new NotImplementedException();
public override IEnumerable<DocumentSnapshot> GetRelatedDocuments(DocumentSnapshot document) => throw new NotImplementedException();
public override RazorProjectEngine GetProjectEngine() => throw new NotImplementedException();
public override Task<IReadOnlyList<TagHelperDescriptor>> GetTagHelpersAsync() => throw new NotImplementedException();

Просмотреть файл

@ -7,29 +7,39 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
internal class ProjectChangeEventArgs : EventArgs
{
public ProjectChangeEventArgs(string projectFilePath, ProjectChangeKind kind)
public ProjectChangeEventArgs(ProjectSnapshot older, ProjectSnapshot newer, ProjectChangeKind kind)
{
if (projectFilePath == null)
if (older == null && newer == null)
{
throw new ArgumentNullException(nameof(projectFilePath));
throw new ArgumentException("Both projects cannot be null.");
}
ProjectFilePath = projectFilePath;
Older = older;
Newer = newer;
Kind = kind;
ProjectFilePath = older?.FilePath ?? newer.FilePath;
}
public ProjectChangeEventArgs(string projectFilePath, string documentFilePath, ProjectChangeKind kind)
public ProjectChangeEventArgs(ProjectSnapshot older, ProjectSnapshot newer, string documentFilePath, ProjectChangeKind kind)
{
if (projectFilePath == null)
if (older == null && newer == null)
{
throw new ArgumentNullException(nameof(projectFilePath));
throw new ArgumentException("Both projects cannot be null.");
}
ProjectFilePath = projectFilePath;
Older = older;
Newer = newer;
DocumentFilePath = documentFilePath;
Kind = kind;
ProjectFilePath = older?.FilePath ?? newer.FilePath;
}
public ProjectSnapshot Older { get; }
public ProjectSnapshot Newer { get; }
public string ProjectFilePath { get; }
public string DocumentFilePath { get; }

Просмотреть файл

@ -2,6 +2,10 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Host;
@ -41,11 +45,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
return this;
}
public RazorProjectEngine GetProjectEngine(ProjectSnapshot snapshot)
public RazorProjectEngine GetProjectEngine(ProjectState state)
{
if (snapshot == null)
if (state == null)
{
throw new ArgumentNullException(nameof(snapshot));
throw new ArgumentNullException(nameof(state));
}
if (_projectEngine == null)
@ -55,12 +59,30 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
if (_projectEngine == null)
{
var factory = _services.GetRequiredService<ProjectSnapshotProjectEngineFactory>();
_projectEngine = factory.Create(snapshot);
_projectEngine = factory.Create(state.HostProject.Configuration, Path.GetDirectoryName(state.HostProject.FilePath), configure: null);
}
}
}
return _projectEngine;
}
public List<string> GetImportDocumentTargetPaths(ProjectState state, string targetPath)
{
var projectEngine = GetProjectEngine(state);
var importFeature = projectEngine.ProjectFeatures.OfType<IImportProjectFeature>().FirstOrDefault();
var projectItem = projectEngine.FileSystem.GetItem(targetPath);
var importItems = importFeature?.GetImports(projectItem).Where(i => i.FilePath != null);
// Target path looks like `Foo\\Bar.cshtml`
var targetPaths = new List<string>();
foreach (var importItem in importItems)
{
var itemTargetPath = importItem.FilePath.Replace('/', '\\').TrimStart('\\');
targetPaths.Add(itemTargetPath);
}
return targetPaths;
}
}
}

Просмотреть файл

@ -25,6 +25,17 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
public abstract DocumentSnapshot GetDocument(string filePath);
public abstract bool IsImportDocument(DocumentSnapshot document);
/// <summary>
/// If the provided document is an import document, gets the other documents in the project
/// that include directives specified by the provided document. Otherwise returns an empty
/// list.
/// </summary>
/// <param name="document">The document.</param>
/// <returns>A list of related documents.</returns>
public abstract IEnumerable<DocumentSnapshot> GetRelatedDocuments(DocumentSnapshot document);
public abstract Task<IReadOnlyList<TagHelperDescriptor>> GetTagHelpersAsync();
public abstract bool TryGetTagHelpers(out IReadOnlyList<TagHelperDescriptor> result);

Просмотреть файл

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Text;
@ -12,10 +13,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// Internal tracker for DefaultProjectSnapshot
internal class ProjectState
{
private static readonly IReadOnlyDictionary<string, DocumentState> EmptyDocuments = new Dictionary<string, DocumentState>();
private static readonly ImmutableDictionary<string, DocumentState> EmptyDocuments = ImmutableDictionary.Create<string, DocumentState>(FilePathComparer.Instance);
private static readonly ImmutableDictionary<string, ImmutableArray<string>> EmptyImportsToRelatedDocuments = ImmutableDictionary.Create<string, ImmutableArray<string>>(FilePathComparer.Instance);
private readonly object _lock;
private ProjectEngineTracker _projectEngine;
private ProjectTagHelperTracker _tagHelpers;
@ -43,6 +44,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
HostProject = hostProject;
WorkspaceProject = workspaceProject;
Documents = EmptyDocuments;
ImportsToRelatedDocuments = EmptyImportsToRelatedDocuments;
Version = VersionStamp.Create();
_lock = new object();
@ -53,7 +55,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
ProjectDifference difference,
HostProject hostProject,
Project workspaceProject,
IReadOnlyDictionary<string, DocumentState> documents)
ImmutableDictionary<string, DocumentState> documents,
ImmutableDictionary<string, ImmutableArray<string>> importsToRelatedDocuments)
{
if (older == null)
{
@ -70,12 +73,18 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
throw new ArgumentNullException(nameof(documents));
}
if (importsToRelatedDocuments == null)
{
throw new ArgumentNullException(nameof(importsToRelatedDocuments));
}
Services = older.Services;
Version = older.Version.GetNewerVersion();
HostProject = hostProject;
WorkspaceProject = workspaceProject;
Documents = documents;
ImportsToRelatedDocuments = importsToRelatedDocuments;
_lock = new object();
@ -84,7 +93,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
}
// Internal set for testing.
public IReadOnlyDictionary<string, DocumentState> Documents { get; internal set; }
public ImmutableDictionary<string, DocumentState> Documents { get; internal set; }
// Internal set for testing.
public ImmutableDictionary<string, ImmutableArray<string>> ImportsToRelatedDocuments { get; internal set; }
public HostProject HostProject { get; }
@ -152,17 +164,24 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
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, DocumentState.Create(Services, hostDocument, loader));
var documents = Documents.Add(hostDocument.FilePath, DocumentState.Create(Services, hostDocument, loader));
var difference = ProjectDifference.DocumentAdded;
var state = new ProjectState(this, difference, HostProject, WorkspaceProject, documents);
// Compute the effect on the import map
var importTargetPaths = ProjectEngine.GetImportDocumentTargetPaths(this, hostDocument.TargetPath);
var importsToRelatedDocuments = AddToImportsToRelatedDocuments(ImportsToRelatedDocuments, hostDocument, importTargetPaths);
// Now check if the updated document is an import - it's important this this happens after
// updating the imports map.
if (importsToRelatedDocuments.TryGetValue(hostDocument.TargetPath, out var relatedDocuments))
{
foreach (var relatedDocument in relatedDocuments)
{
documents = documents.SetItem(relatedDocument, documents[relatedDocument].WithImportsChange());
}
}
var state = new ProjectState(this, ProjectDifference.DocumentAdded, HostProject, WorkspaceProject, documents, importsToRelatedDocuments);
return state;
}
@ -177,17 +196,24 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
return this;
}
var documents = Documents.Remove(hostDocument.FilePath);
var documents = new Dictionary<string, DocumentState>(FilePathComparer.Instance);
foreach (var kvp in Documents)
// First check if the updated document is an import - it's important that this happens
// before updating the imports map.
if (ImportsToRelatedDocuments.TryGetValue(hostDocument.TargetPath, out var relatedDocuments))
{
documents.Add(kvp.Key, kvp.Value);
foreach (var relatedDocument in relatedDocuments)
{
documents = documents.SetItem(relatedDocument, documents[relatedDocument].WithImportsChange());
}
}
documents.Remove(hostDocument.FilePath);
// Compute the effect on the import map
var importTargetPaths = ProjectEngine.GetImportDocumentTargetPaths(this, hostDocument.TargetPath);
var importsToRelatedDocuments = RemoveFromImportsToRelatedDocuments(ImportsToRelatedDocuments, hostDocument, importTargetPaths);
var difference = ProjectDifference.DocumentRemoved;
var state = new ProjectState(this, difference, HostProject, WorkspaceProject, documents);
var state = new ProjectState(this, ProjectDifference.DocumentRemoved, HostProject, WorkspaceProject, documents, importsToRelatedDocuments);
return state;
}
@ -198,23 +224,22 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
throw new ArgumentNullException(nameof(hostDocument));
}
if (!Documents.ContainsKey(hostDocument.FilePath))
if (!Documents.TryGetValue(hostDocument.FilePath, out var document))
{
return this;
}
var documents = new Dictionary<string, DocumentState>(FilePathComparer.Instance);
foreach (var kvp in Documents)
var documents = Documents.SetItem(hostDocument.FilePath, document.WithText(sourceText, version));
if (ImportsToRelatedDocuments.TryGetValue(hostDocument.TargetPath, out var relatedDocuments))
{
documents.Add(kvp.Key, kvp.Value);
foreach (var relatedDocument in relatedDocuments)
{
documents = documents.SetItem(relatedDocument, documents[relatedDocument].WithImportsChange());
}
}
if (documents.TryGetValue(hostDocument.FilePath, out var document))
{
documents[hostDocument.FilePath] = document.WithText(sourceText, version);
}
var state = new ProjectState(this, ProjectDifference.DocumentChanged, HostProject, WorkspaceProject, documents);
var state = new ProjectState(this, ProjectDifference.DocumentChanged, HostProject, WorkspaceProject, documents, ImportsToRelatedDocuments);
return state;
}
@ -225,23 +250,22 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
throw new ArgumentNullException(nameof(hostDocument));
}
if (!Documents.ContainsKey(hostDocument.FilePath))
if (!Documents.TryGetValue(hostDocument.FilePath, out var document))
{
return this;
}
var documents = new Dictionary<string, DocumentState>(FilePathComparer.Instance);
foreach (var kvp in Documents)
var documents = Documents.SetItem(hostDocument.FilePath, document.WithTextLoader(loader));
if (ImportsToRelatedDocuments.TryGetValue(hostDocument.TargetPath, out var relatedDocuments))
{
documents.Add(kvp.Key, kvp.Value);
foreach (var relatedDocument in relatedDocuments)
{
documents = documents.SetItem(relatedDocument, documents[relatedDocument].WithImportsChange());
}
}
if (documents.TryGetValue(hostDocument.FilePath, out var document))
{
documents[hostDocument.FilePath] = document.WithTextLoader(loader);
}
var state = new ProjectState(this, ProjectDifference.DocumentChanged, HostProject, WorkspaceProject, documents);
var state = new ProjectState(this, ProjectDifference.DocumentChanged, HostProject, WorkspaceProject, documents, ImportsToRelatedDocuments);
return state;
}
@ -256,15 +280,20 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
return this;
}
var documents = Documents.ToImmutableDictionary(kvp => kvp.Key, kvp => kvp.Value.WithConfigurationChange(), FilePathComparer.Instance);
var difference = ProjectDifference.ConfigurationChanged;
var documents = new Dictionary<string, DocumentState>(FilePathComparer.Instance);
foreach (var kvp in Documents)
// If the host project has changed then we need to recompute the imports map
var importsToRelatedDocuments = EmptyImportsToRelatedDocuments;
foreach (var document in documents)
{
documents.Add(kvp.Key, kvp.Value.WithConfigurationChange());
var importTargetPaths = ProjectEngine.GetImportDocumentTargetPaths(this, document.Value.HostDocument.TargetPath);
importsToRelatedDocuments = AddToImportsToRelatedDocuments(ImportsToRelatedDocuments, document.Value.HostDocument, importTargetPaths);
}
var state = new ProjectState(this, difference, hostProject, WorkspaceProject, documents);
var state = new ProjectState(this, ProjectDifference.ConfigurationChanged, hostProject, WorkspaceProject, documents, importsToRelatedDocuments);
return state;
}
@ -291,14 +320,52 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
return this;
}
var documents = new Dictionary<string, DocumentState>(FilePathComparer.Instance);
foreach (var kvp in Documents)
var documents = Documents.ToImmutableDictionary(kvp => kvp.Key, kvp => kvp.Value.WithWorkspaceProjectChange(), FilePathComparer.Instance);
var state = new ProjectState(this, difference, HostProject, workspaceProject, documents, ImportsToRelatedDocuments);
return state;
}
private static ImmutableDictionary<string, ImmutableArray<string>> AddToImportsToRelatedDocuments(
ImmutableDictionary<string, ImmutableArray<string>> importsToRelatedDocuments,
HostDocument hostDocument,
List<string> importTargetPaths)
{
foreach (var importTargetPath in importTargetPaths)
{
documents.Add(kvp.Key, kvp.Value.WithWorkspaceProjectChange());
if (!importsToRelatedDocuments.TryGetValue(importTargetPath, out var relatedDocuments))
{
relatedDocuments = ImmutableArray.Create<string>();
}
relatedDocuments = relatedDocuments.Add(hostDocument.FilePath);
importsToRelatedDocuments = importsToRelatedDocuments.SetItem(importTargetPath, relatedDocuments);
}
var state = new ProjectState(this, difference, HostProject, workspaceProject, documents);
return state;
return importsToRelatedDocuments;
}
private static ImmutableDictionary<string, ImmutableArray<string>> RemoveFromImportsToRelatedDocuments(
ImmutableDictionary<string, ImmutableArray<string>> importsToRelatedDocuments,
HostDocument hostDocument,
List<string> importTargetPaths)
{
foreach (var importTargetPath in importTargetPaths)
{
if (importsToRelatedDocuments.TryGetValue(importTargetPath, out var relatedDocuments))
{
relatedDocuments = relatedDocuments.Remove(hostDocument.FilePath);
if (relatedDocuments.Length > 0)
{
importsToRelatedDocuments = importsToRelatedDocuments.SetItem(importTargetPath, relatedDocuments);
}
else
{
importsToRelatedDocuments = importsToRelatedDocuments.Remove(importTargetPath);
}
}
}
return importsToRelatedDocuments;
}
}
}

Просмотреть файл

@ -76,6 +76,16 @@ namespace Microsoft.CodeAnalysis.Remote.Razor
return null;
}
public override bool IsImportDocument(DocumentSnapshot document)
{
throw new NotImplementedException();
}
public override IEnumerable<DocumentSnapshot> GetRelatedDocuments(DocumentSnapshot document)
{
throw new NotImplementedException();
}
public override RazorProjectEngine GetProjectEngine()
{
throw new NotImplementedException();

Просмотреть файл

@ -237,7 +237,7 @@ namespace Microsoft.CodeAnalysis.Razor
{
case ProjectChangeKind.ProjectAdded:
{
var projectSnapshot = _projectManager.GetLoadedProject(e.ProjectFilePath);
var projectSnapshot = e.Newer;
foreach (var documentFilePath in projectSnapshot.DocumentFilePaths)
{
Enqueue(projectSnapshot, projectSnapshot.GetDocument(documentFilePath));
@ -247,7 +247,7 @@ namespace Microsoft.CodeAnalysis.Razor
}
case ProjectChangeKind.ProjectChanged:
{
var projectSnapshot = _projectManager.GetLoadedProject(e.ProjectFilePath);
var projectSnapshot = e.Newer;
foreach (var documentFilePath in projectSnapshot.DocumentFilePaths)
{
Enqueue(projectSnapshot, projectSnapshot.GetDocument(documentFilePath));
@ -257,23 +257,35 @@ namespace Microsoft.CodeAnalysis.Razor
}
case ProjectChangeKind.DocumentAdded:
case ProjectChangeKind.DocumentChanged:
{
var project = _projectManager.GetLoadedProject(e.ProjectFilePath);
Enqueue(project, project.GetDocument(e.DocumentFilePath));
var project = e.Newer;
var document = project.GetDocument(e.DocumentFilePath);
Enqueue(project, document);
foreach (var relatedDocument in project.GetRelatedDocuments(document))
{
Enqueue(project, document);
}
break;
}
case ProjectChangeKind.DocumentChanged:
case ProjectChangeKind.DocumentRemoved:
{
var project = _projectManager.GetLoadedProject(e.ProjectFilePath);
Enqueue(project, project.GetDocument(e.DocumentFilePath));
// For removals use the old snapshot to find the removed document, so we can figure out
// what the imports were in the new snapshot.
var document = e.Older.GetDocument(e.DocumentFilePath);
foreach (var relatedDocument in e.Newer.GetRelatedDocuments(document))
{
Enqueue(e.Newer, document);
}
break;
}
case ProjectChangeKind.ProjectRemoved:
case ProjectChangeKind.DocumentRemoved:
{
// ignore
break;

Просмотреть файл

@ -1,36 +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.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Text;
using Xunit;
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
public class DefaultDocumentSnapshotTest
public class DefaultDocumentSnapshotTest : WorkspaceTestBase
{
public DefaultDocumentSnapshotTest()
{
var services = TestServices.Create(
new[] { new TestProjectSnapshotProjectEngineFactory() },
new[] { new TestTagHelperResolver() });
Workspace = TestWorkspace.Create(services);
var hostProject = new HostProject("C:/some/path/project.csproj", RazorConfiguration.Default);
var projectState = ProjectState.Create(Workspace.Services, hostProject);
var projectState = ProjectState.Create(Workspace.Services, TestProjectData.SomeProject);
var project = new DefaultProjectSnapshot(projectState);
HostDocument = new HostDocument("C:/some/path/file.cshtml", "C:/some/path/file.cshtml");
SourceText = Text.SourceText.From("<p>Hello World</p>");
HostDocument = TestProjectData.SomeProjectFile1;
SourceText = SourceText.From("<p>Hello World</p>");
Version = VersionStamp.Default.GetNewerVersion();
var textAndVersion = TextAndVersion.Create(SourceText, Version);
var documentState = DocumentState.Create(Workspace.Services, HostDocument, () => Task.FromResult(textAndVersion));
Document = new DefaultDocumentSnapshot(project, documentState);
}
private Workspace Workspace { get; }
private SourceText SourceText { get; }
private VersionStamp Version { get; }
@ -39,6 +31,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
private DefaultDocumentSnapshot Document { get; }
protected override void ConfigureLanguageServices(List<ILanguageService> services)
{
services.Add(new TestTagHelperResolver());
}
[Fact]
public async Task GetGeneratedOutputAsync_SetsHostDocumentOutput()
{

Просмотреть файл

@ -1,35 +1,23 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Host;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Host;
using Xunit;
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
public class DefaultProjectSnapshotTest
public class DefaultProjectSnapshotTest : WorkspaceTestBase
{
public DefaultProjectSnapshotTest()
{
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);
HostProject = new HostProject(TestProjectData.SomeProject.FilePath, FallbackRazorConfiguration.MVC_2_0);
HostProjectWithConfigurationChange = new HostProject(TestProjectData.SomeProject.FilePath, FallbackRazorConfiguration.MVC_1_0);
var projectId = ProjectId.CreateNewId("Test");
var solution = Workspace.CurrentSolution.AddProject(ProjectInfo.Create(
@ -38,19 +26,21 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
"Test",
"Test",
LanguageNames.CSharp,
"c:\\MyProject\\Test.csproj"));
TestProjectData.SomeProject.FilePath));
WorkspaceProject = solution.GetProject(projectId);
SomeTagHelpers = new List<TagHelperDescriptor>();
SomeTagHelpers.Add(TagHelperDescriptorBuilder.Create("Test1", "TestAssembly").Build());
SomeTagHelpers = new List<TagHelperDescriptor>
{
TagHelperDescriptorBuilder.Create("Test1", "TestAssembly").Build()
};
Documents = new HostDocument[]
{
new HostDocument("c:\\MyProject\\File.cshtml", "File.cshtml"),
new HostDocument("c:\\MyProject\\Index.cshtml", "Index.cshtml"),
TestProjectData.SomeProjectFile1,
TestProjectData.SomeProjectFile2,
// linked file
new HostDocument("c:\\SomeOtherProject\\Index.cshtml", "Pages\\Index.cshtml"),
TestProjectData.AnotherProjectNestedFile3,
};
}
@ -64,12 +54,25 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
private TestTagHelperResolver TagHelperResolver { get; }
private HostServices HostServices { get; }
private Workspace Workspace { get; }
private List<TagHelperDescriptor> SomeTagHelpers { get; }
private void Configure(RazorProjectEngineBuilder builder)
{
builder.Features.Remove(builder.Features.OfType<IImportProjectFeature>().Single());
builder.Features.Add(new TestImportProjectFeature());
}
protected override void ConfigureLanguageServices(List<ILanguageService> services)
{
services.Add(TagHelperResolver);
}
protected override void ConfigureProjectEngine(RazorProjectEngineBuilder builder)
{
builder.Features.Remove(builder.Features.OfType<IImportProjectFeature>().Single());
builder.Features.Add(new TestImportProjectFeature());
}
[Fact]
public void ProjectSnapshot_CachesDocumentSnapshots()
{
@ -114,5 +117,79 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
TagHelperResolver.CompletionSource.SetCanceled();
}
}
[Fact]
public void IsImportDocument_NonImportDocument_ReturnsFalse()
{
// Arrange
var state = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject)
.WithAddedHostDocument(Documents[0], DocumentState.EmptyLoader);
var snapshot = new DefaultProjectSnapshot(state);
var document = snapshot.GetDocument(Documents[0].FilePath);
// Act
var result = snapshot.IsImportDocument(document);
// Assert
Assert.False(result);
}
[Fact]
public void IsImportDocument_ImportDocument_ReturnsTrue()
{
// Arrange
var state = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject)
.WithAddedHostDocument(Documents[0], DocumentState.EmptyLoader)
.WithAddedHostDocument(TestProjectData.SomeProjectImportFile, DocumentState.EmptyLoader);
var snapshot = new DefaultProjectSnapshot(state);
var document = snapshot.GetDocument(TestProjectData.SomeProjectImportFile.FilePath);
// Act
var result = snapshot.IsImportDocument(document);
// Assert
Assert.True(result);
}
[Fact]
public void GetRelatedDocuments_NonImportDocument_ReturnsEmpty()
{
// Arrange
var state = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject)
.WithAddedHostDocument(Documents[0], DocumentState.EmptyLoader);
var snapshot = new DefaultProjectSnapshot(state);
var document = snapshot.GetDocument(Documents[0].FilePath);
// Act
var documents = snapshot.GetRelatedDocuments(document);
// Assert
Assert.Empty(documents);
}
[Fact]
public void GetRelatedDocuments_ImportDocument_ReturnsRelated()
{
// Arrange
var state = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject)
.WithAddedHostDocument(Documents[0], DocumentState.EmptyLoader)
.WithAddedHostDocument(Documents[1], DocumentState.EmptyLoader)
.WithAddedHostDocument(TestProjectData.SomeProjectImportFile, DocumentState.EmptyLoader);
var snapshot = new DefaultProjectSnapshot(state);
var document = snapshot.GetDocument(TestProjectData.SomeProjectImportFile.FilePath);
// Act
var documents = snapshot.GetRelatedDocuments(document);
// Assert
Assert.Collection(
documents.OrderBy(d => d.FilePath),
d => Assert.Equal(Documents[0].FilePath, d.FilePath),
d => Assert.Equal(Documents[1].FilePath, d.FilePath));
}
}
}

Просмотреть файл

@ -3,37 +3,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Text;
using Moq;
using Xunit;
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
public class DocumentStateTest
public class DocumentStateTest : WorkspaceTestBase
{
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);
HostProject = new HostProject(TestProjectData.SomeProject.FilePath, FallbackRazorConfiguration.MVC_2_0);
HostProjectWithConfigurationChange = new HostProject(TestProjectData.SomeProject.FilePath, FallbackRazorConfiguration.MVC_1_0);
var projectId = ProjectId.CreateNewId("Test");
var solution = Workspace.CurrentSolution.AddProject(ProjectInfo.Create(
@ -42,13 +27,13 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
"Test",
"Test",
LanguageNames.CSharp,
"c:\\MyProject\\Test.csproj"));
TestProjectData.SomeProject.FilePath));
WorkspaceProject = solution.GetProject(projectId);
SomeTagHelpers = new List<TagHelperDescriptor>();
SomeTagHelpers.Add(TagHelperDescriptorBuilder.Create("Test1", "TestAssembly").Build());
Document = new HostDocument("c:\\MyProject\\File.cshtml", "File.cshtml");
Document = TestProjectData.SomeProjectFile1;
Text = SourceText.From("Hello, world!");
TextLoader = () => Task.FromResult(TextAndVersion.Create(Text, VersionStamp.Create()));
@ -64,16 +49,17 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
private TestTagHelperResolver TagHelperResolver { get; }
private HostServices HostServices { get; }
private Workspace Workspace { get; }
private List<TagHelperDescriptor> SomeTagHelpers { get; }
private Func<Task<TextAndVersion>> TextLoader { get; }
private SourceText Text { get; }
protected override void ConfigureLanguageServices(List<ILanguageService> services)
{
services.Add(TagHelperResolver);
}
[Fact]
public async Task DocumentState_CreatedNew_HasEmptyText()
{
@ -145,6 +131,38 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
Assert.True(state.TryGetTextVersion(out _));
}
[Fact]
public void DocumentState_WithImportsChange_CachesSnapshotText()
{
// Arrange
var original = DocumentState.Create(Workspace.Services, Document, DocumentState.EmptyLoader)
.WithText(Text, VersionStamp.Create());
// Act
var state = original.WithImportsChange();
// Assert
Assert.True(state.TryGetText(out _));
Assert.True(state.TryGetTextVersion(out _));
}
[Fact]
public async Task DocumentState_WithImportsChange_CachesLoadedText()
{
// Arrange
var original = DocumentState.Create(Workspace.Services, Document, DocumentState.EmptyLoader)
.WithTextLoader(TextLoader);
await original.GetTextAsync();
// Act
var state = original.WithImportsChange();
// Assert
Assert.True(state.TryGetText(out _));
Assert.True(state.TryGetTextVersion(out _));
}
[Fact]
public void DocumentState_WithWorkspaceProjectChange_CachesSnapshotText()
{
@ -160,7 +178,6 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
Assert.True(state.TryGetTextVersion(out _));
}
[Fact]
public async Task DocumentState_WithWorkspaceProjectChange_CachesLoadedText()
{

Просмотреть файл

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
@ -12,26 +13,14 @@ using Xunit;
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
public class ProjectStateTest
public class ProjectStateTest : WorkspaceTestBase
{
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);
HostProject = new HostProject(TestProjectData.SomeProject.FilePath, FallbackRazorConfiguration.MVC_2_0);
HostProjectWithConfigurationChange = new HostProject(TestProjectData.SomeProject.FilePath, FallbackRazorConfiguration.MVC_1_0);
var projectId = ProjectId.CreateNewId("Test");
var solution = Workspace.CurrentSolution.AddProject(ProjectInfo.Create(
@ -40,7 +29,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
"Test",
"Test",
LanguageNames.CSharp,
"c:\\MyProject\\Test.csproj"));
TestProjectData.SomeProject.FilePath));
WorkspaceProject = solution.GetProject(projectId);
SomeTagHelpers = new List<TagHelperDescriptor>();
@ -48,11 +37,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
Documents = new HostDocument[]
{
new HostDocument("c:\\MyProject\\File.cshtml", "File.cshtml"),
new HostDocument("c:\\MyProject\\Index.cshtml", "Index.cshtml"),
TestProjectData.SomeProjectFile1,
TestProjectData.SomeProjectFile2,
// linked file
new HostDocument("c:\\SomeOtherProject\\Index.cshtml", "Pages\\Index.cshtml"),
TestProjectData.AnotherProjectNestedFile3,
};
Text = SourceText.From("Hello, world!");
@ -69,16 +58,23 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
private TestTagHelperResolver TagHelperResolver { get; }
private HostServices HostServices { get; }
private Workspace Workspace { get; }
private List<TagHelperDescriptor> SomeTagHelpers { get; }
private Func<Task<TextAndVersion>> TextLoader { get; }
private SourceText Text { get; }
protected override void ConfigureLanguageServices(List<ILanguageService> services)
{
services.Add(TagHelperResolver);
}
protected override void ConfigureProjectEngine(RazorProjectEngineBuilder builder)
{
builder.Features.Remove(builder.Features.OfType<IImportProjectFeature>().Single());
builder.Features.Add(new TestImportProjectFeature());
}
[Fact]
public void ProjectState_ConstructedNew()
{
@ -139,9 +135,93 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
Assert.Collection(
state.Documents.OrderBy(kvp => kvp.Key),
d => Assert.Same(Documents[2], d.Value.HostDocument),
d => Assert.Same(Documents[0], d.Value.HostDocument),
d => Assert.Same(Documents[1], d.Value.HostDocument),
d => Assert.Same(Documents[2], d.Value.HostDocument));
d => Assert.Same(Documents[1], d.Value.HostDocument));
}
[Fact]
public void ProjectState_AddHostDocument_TracksImports()
{
// Arrange
// Act
var state = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject)
.WithAddedHostDocument(TestProjectData.SomeProjectFile1, DocumentState.EmptyLoader)
.WithAddedHostDocument(TestProjectData.SomeProjectFile2, DocumentState.EmptyLoader)
.WithAddedHostDocument(TestProjectData.SomeProjectNestedFile3, DocumentState.EmptyLoader)
.WithAddedHostDocument(TestProjectData.AnotherProjectNestedFile4, DocumentState.EmptyLoader);
// Assert
Assert.Collection(
state.ImportsToRelatedDocuments.OrderBy(kvp => kvp.Key),
kvp =>
{
Assert.Equal(TestProjectData.SomeProjectImportFile.TargetPath, kvp.Key);
Assert.Equal(
new string[]
{
TestProjectData.AnotherProjectNestedFile4.FilePath,
TestProjectData.SomeProjectFile1.FilePath,
TestProjectData.SomeProjectFile2.FilePath,
TestProjectData.SomeProjectNestedFile3.FilePath,
},
kvp.Value.OrderBy(f => f));
},
kvp =>
{
Assert.Equal(TestProjectData.SomeProjectNestedImportFile.TargetPath, kvp.Key);
Assert.Equal(
new string[]
{
TestProjectData.AnotherProjectNestedFile4.FilePath,
TestProjectData.SomeProjectNestedFile3.FilePath,
},
kvp.Value.OrderBy(f => f));
});
}
[Fact]
public void ProjectState_AddHostDocument_TracksImports_AddImportFile()
{
// Arrange
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject)
.WithAddedHostDocument(TestProjectData.SomeProjectFile1, DocumentState.EmptyLoader)
.WithAddedHostDocument(TestProjectData.SomeProjectFile2, DocumentState.EmptyLoader)
.WithAddedHostDocument(TestProjectData.SomeProjectNestedFile3, DocumentState.EmptyLoader)
.WithAddedHostDocument(TestProjectData.AnotherProjectNestedFile4, DocumentState.EmptyLoader);
// Act
var state = original
.WithAddedHostDocument(TestProjectData.AnotherProjectImportFile, DocumentState.EmptyLoader);
// Assert
Assert.Collection(
state.ImportsToRelatedDocuments.OrderBy(kvp => kvp.Key),
kvp =>
{
Assert.Equal(TestProjectData.SomeProjectImportFile.TargetPath, kvp.Key);
Assert.Equal(
new string[]
{
TestProjectData.AnotherProjectNestedFile4.FilePath,
TestProjectData.SomeProjectFile1.FilePath,
TestProjectData.SomeProjectFile2.FilePath,
TestProjectData.SomeProjectNestedFile3.FilePath,
},
kvp.Value.OrderBy(f => f));
},
kvp =>
{
Assert.Equal(TestProjectData.SomeProjectNestedImportFile.TargetPath, kvp.Key);
Assert.Equal(
new string[]
{
TestProjectData.AnotherProjectNestedFile4.FilePath,
TestProjectData.SomeProjectNestedFile3.FilePath,
},
kvp.Value.OrderBy(f => f));
});
}
[Fact]
@ -311,6 +391,68 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
d => Assert.Same(Documents[2], d.Value.HostDocument));
}
[Fact]
public void ProjectState_RemoveHostDocument_TracksImports()
{
// Arrange
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject)
.WithAddedHostDocument(TestProjectData.SomeProjectFile1, DocumentState.EmptyLoader)
.WithAddedHostDocument(TestProjectData.SomeProjectFile2, DocumentState.EmptyLoader)
.WithAddedHostDocument(TestProjectData.SomeProjectNestedFile3, DocumentState.EmptyLoader)
.WithAddedHostDocument(TestProjectData.AnotherProjectNestedFile4, DocumentState.EmptyLoader);
// Act
var state = original.WithRemovedHostDocument(TestProjectData.SomeProjectNestedFile3);
// Assert
Assert.Collection(
state.ImportsToRelatedDocuments.OrderBy(kvp => kvp.Key),
kvp =>
{
Assert.Equal(TestProjectData.SomeProjectImportFile.TargetPath, kvp.Key);
Assert.Equal(
new string[]
{
TestProjectData.AnotherProjectNestedFile4.FilePath,
TestProjectData.SomeProjectFile1.FilePath,
TestProjectData.SomeProjectFile2.FilePath,
},
kvp.Value.OrderBy(f => f));
},
kvp =>
{
Assert.Equal(TestProjectData.SomeProjectNestedImportFile.TargetPath, kvp.Key);
Assert.Equal(
new string[]
{
TestProjectData.AnotherProjectNestedFile4.FilePath,
},
kvp.Value.OrderBy(f => f));
});
}
[Fact]
public void ProjectState_RemoveHostDocument_TracksImports_RemoveAllDocuments()
{
// Arrange
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject)
.WithAddedHostDocument(TestProjectData.SomeProjectFile1, DocumentState.EmptyLoader)
.WithAddedHostDocument(TestProjectData.SomeProjectFile2, DocumentState.EmptyLoader)
.WithAddedHostDocument(TestProjectData.SomeProjectNestedFile3, DocumentState.EmptyLoader)
.WithAddedHostDocument(TestProjectData.AnotherProjectNestedFile4, DocumentState.EmptyLoader);
// Act
var state = original
.WithRemovedHostDocument(TestProjectData.SomeProjectFile1)
.WithRemovedHostDocument(TestProjectData.SomeProjectFile2)
.WithRemovedHostDocument(TestProjectData.SomeProjectNestedFile3)
.WithRemovedHostDocument(TestProjectData.AnotherProjectNestedFile4);
// Assert
Assert.Empty(state.Documents);
Assert.Empty(state.ImportsToRelatedDocuments);
}
[Fact]
public void ProjectState_RemoveHostDocument_RetainsComputedState()
{
@ -398,13 +540,13 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
// Arrange
var callCount = 0;
var documents = new Dictionary<string, DocumentState>();
var documents = ImmutableDictionary.CreateBuilder<string, DocumentState>(FilePathComparer.Instance);
documents[Documents[1].FilePath] = TestDocumentState.Create(Workspace.Services, Documents[1], onConfigurationChange: () => callCount++);
documents[Documents[2].FilePath] = TestDocumentState.Create(Workspace.Services, Documents[2], onConfigurationChange: () => callCount++);
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject);
original.Documents = documents;
original.Documents = documents.ToImmutable();
var changed = WorkspaceProject.WithAssemblyName("Test1");
@ -503,12 +645,12 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// Arrange
var callCount = 0;
var documents = new Dictionary<string, DocumentState>();
var documents = ImmutableDictionary.CreateBuilder<string, DocumentState>(FilePathComparer.Instance);
documents[Documents[1].FilePath] = TestDocumentState.Create(Workspace.Services, Documents[1], onWorkspaceProjectChange: () => callCount++);
documents[Documents[2].FilePath] = TestDocumentState.Create(Workspace.Services, Documents[2], onWorkspaceProjectChange: () => callCount++);
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject);
original.Documents = documents;
original.Documents = documents.ToImmutable();
var changed = WorkspaceProject.WithAssemblyName("Test1");
@ -520,6 +662,231 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
Assert.Equal(2, callCount);
}
[Fact]
public void ProjectState_WhenImportDocumentAdded_CallsImportsChanged()
{
// Arrange
var callCount = 0;
var document1 = TestProjectData.SomeProjectFile1;
var document2 = TestProjectData.SomeProjectFile2;
var document3 = TestProjectData.SomeProjectNestedFile3;
var document4 = TestProjectData.AnotherProjectNestedFile4;
var documents = ImmutableDictionary.CreateBuilder<string, DocumentState>(FilePathComparer.Instance);
documents[document1.FilePath] = TestDocumentState.Create(Workspace.Services, document1, onImportsChange: () => callCount++);
documents[document2.FilePath] = TestDocumentState.Create(Workspace.Services, document2, onImportsChange: () => callCount++);
documents[document3.FilePath] = TestDocumentState.Create(Workspace.Services, document3, onImportsChange: () => callCount++);
documents[document4.FilePath] = TestDocumentState.Create(Workspace.Services, document4, onImportsChange: () => callCount++);
var importsToRelatedDocuments = ImmutableDictionary.CreateBuilder<string, ImmutableArray<string>>(FilePathComparer.Instance);
importsToRelatedDocuments.Add(
TestProjectData.SomeProjectImportFile.TargetPath,
ImmutableArray.Create(
TestProjectData.SomeProjectFile1.FilePath,
TestProjectData.SomeProjectFile2.FilePath,
TestProjectData.SomeProjectNestedFile3.FilePath,
TestProjectData.AnotherProjectNestedFile4.FilePath));
importsToRelatedDocuments.Add(
TestProjectData.SomeProjectNestedImportFile.TargetPath,
ImmutableArray.Create(
TestProjectData.SomeProjectNestedFile3.FilePath,
TestProjectData.AnotherProjectNestedFile4.FilePath));
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject);
original.Documents = documents.ToImmutable();
original.ImportsToRelatedDocuments = importsToRelatedDocuments.ToImmutable();
// Act
var state = original.WithAddedHostDocument(TestProjectData.AnotherProjectImportFile, DocumentState.EmptyLoader);
// Assert
Assert.NotEqual(original.Version, state.Version);
Assert.Equal(4, callCount);
}
[Fact]
public void ProjectState_WhenImportDocumentAdded_CallsImportsChanged_Nested()
{
// Arrange
var callCount = 0;
var document1 = TestProjectData.SomeProjectFile1;
var document2 = TestProjectData.SomeProjectFile2;
var document3 = TestProjectData.SomeProjectNestedFile3;
var document4 = TestProjectData.AnotherProjectNestedFile4;
var documents = ImmutableDictionary.CreateBuilder<string, DocumentState>(FilePathComparer.Instance);
documents[document1.FilePath] = TestDocumentState.Create(Workspace.Services, document1, onImportsChange: () => callCount++);
documents[document2.FilePath] = TestDocumentState.Create(Workspace.Services, document2, onImportsChange: () => callCount++);
documents[document3.FilePath] = TestDocumentState.Create(Workspace.Services, document3, onImportsChange: () => callCount++);
documents[document4.FilePath] = TestDocumentState.Create(Workspace.Services, document4, onImportsChange: () => callCount++);
var importsToRelatedDocuments = ImmutableDictionary.CreateBuilder<string, ImmutableArray<string>>(FilePathComparer.Instance);
importsToRelatedDocuments.Add(
TestProjectData.SomeProjectImportFile.TargetPath,
ImmutableArray.Create(
TestProjectData.SomeProjectFile1.FilePath,
TestProjectData.SomeProjectFile2.FilePath,
TestProjectData.SomeProjectNestedFile3.FilePath,
TestProjectData.AnotherProjectNestedFile4.FilePath));
importsToRelatedDocuments.Add(
TestProjectData.SomeProjectNestedImportFile.TargetPath,
ImmutableArray.Create(
TestProjectData.SomeProjectNestedFile3.FilePath,
TestProjectData.AnotherProjectNestedFile4.FilePath));
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject);
original.Documents = documents.ToImmutable();
original.ImportsToRelatedDocuments = importsToRelatedDocuments.ToImmutable();
// Act
var state = original.WithAddedHostDocument(TestProjectData.AnotherProjectNestedImportFile, DocumentState.EmptyLoader);
// Assert
Assert.NotEqual(original.Version, state.Version);
Assert.Equal(2, callCount);
}
[Fact]
public void ProjectState_WhenImportDocumentChangedTextLoader_CallsImportsChanged()
{
// Arrange
var callCount = 0;
var document1 = TestProjectData.SomeProjectFile1;
var document2 = TestProjectData.SomeProjectFile2;
var document3 = TestProjectData.SomeProjectNestedFile3;
var document4 = TestProjectData.AnotherProjectNestedFile4;
var document5 = TestProjectData.AnotherProjectNestedImportFile;
var documents = ImmutableDictionary.CreateBuilder<string, DocumentState>(FilePathComparer.Instance);
documents[document1.FilePath] = TestDocumentState.Create(Workspace.Services, document1, onImportsChange: () => callCount++);
documents[document2.FilePath] = TestDocumentState.Create(Workspace.Services, document2, onImportsChange: () => callCount++);
documents[document3.FilePath] = TestDocumentState.Create(Workspace.Services, document3, onImportsChange: () => callCount++);
documents[document4.FilePath] = TestDocumentState.Create(Workspace.Services, document4, onImportsChange: () => callCount++);
documents[document5.FilePath] = TestDocumentState.Create(Workspace.Services, document5, onImportsChange: () => callCount++);
var importsToRelatedDocuments = ImmutableDictionary.CreateBuilder<string, ImmutableArray<string>>(FilePathComparer.Instance);
importsToRelatedDocuments.Add(
TestProjectData.SomeProjectImportFile.TargetPath,
ImmutableArray.Create(
TestProjectData.SomeProjectFile1.FilePath,
TestProjectData.SomeProjectFile2.FilePath,
TestProjectData.SomeProjectNestedFile3.FilePath,
TestProjectData.AnotherProjectNestedFile4.FilePath,
TestProjectData.AnotherProjectNestedImportFile.FilePath));
importsToRelatedDocuments.Add(
TestProjectData.SomeProjectNestedImportFile.TargetPath,
ImmutableArray.Create(
TestProjectData.SomeProjectNestedFile3.FilePath,
TestProjectData.AnotherProjectNestedFile4.FilePath));
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject);
original.Documents = documents.ToImmutable();
original.ImportsToRelatedDocuments = importsToRelatedDocuments.ToImmutable();
// Act
var state = original.WithChangedHostDocument(document5, DocumentState.EmptyLoader);
// Assert
Assert.NotEqual(original.Version, state.Version);
Assert.Equal(2, callCount);
}
[Fact]
public void ProjectState_WhenImportDocumentChangedSnapshot_CallsImportsChanged()
{
// Arrange
var callCount = 0;
var document1 = TestProjectData.SomeProjectFile1;
var document2 = TestProjectData.SomeProjectFile2;
var document3 = TestProjectData.SomeProjectNestedFile3;
var document4 = TestProjectData.AnotherProjectNestedFile4;
var document5 = TestProjectData.AnotherProjectNestedImportFile;
var documents = ImmutableDictionary.CreateBuilder<string, DocumentState>(FilePathComparer.Instance);
documents[document1.FilePath] = TestDocumentState.Create(Workspace.Services, document1, onImportsChange: () => callCount++);
documents[document2.FilePath] = TestDocumentState.Create(Workspace.Services, document2, onImportsChange: () => callCount++);
documents[document3.FilePath] = TestDocumentState.Create(Workspace.Services, document3, onImportsChange: () => callCount++);
documents[document4.FilePath] = TestDocumentState.Create(Workspace.Services, document4, onImportsChange: () => callCount++);
documents[document5.FilePath] = TestDocumentState.Create(Workspace.Services, document5, onImportsChange: () => callCount++);
var importsToRelatedDocuments = ImmutableDictionary.CreateBuilder<string, ImmutableArray<string>>(FilePathComparer.Instance);
importsToRelatedDocuments.Add(
TestProjectData.SomeProjectImportFile.TargetPath,
ImmutableArray.Create(
TestProjectData.SomeProjectFile1.FilePath,
TestProjectData.SomeProjectFile2.FilePath,
TestProjectData.SomeProjectNestedFile3.FilePath,
TestProjectData.AnotherProjectNestedFile4.FilePath,
TestProjectData.AnotherProjectNestedImportFile.FilePath));
importsToRelatedDocuments.Add(
TestProjectData.SomeProjectNestedImportFile.TargetPath,
ImmutableArray.Create(
TestProjectData.SomeProjectNestedFile3.FilePath,
TestProjectData.AnotherProjectNestedFile4.FilePath));
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject);
original.Documents = documents.ToImmutable();
original.ImportsToRelatedDocuments = importsToRelatedDocuments.ToImmutable();
// Act
var state = original.WithChangedHostDocument(document5, Text, VersionStamp.Create());
// Assert
Assert.NotEqual(original.Version, state.Version);
Assert.Equal(2, callCount);
}
[Fact]
public void ProjectState_WhenImportDocumentRemoved_CallsImportsChanged()
{
// Arrange
var callCount = 0;
var document1 = TestProjectData.SomeProjectFile1;
var document2 = TestProjectData.SomeProjectFile2;
var document3 = TestProjectData.SomeProjectNestedFile3;
var document4 = TestProjectData.AnotherProjectNestedFile4;
var document5 = TestProjectData.AnotherProjectNestedImportFile;
var documents = ImmutableDictionary.CreateBuilder<string, DocumentState>(FilePathComparer.Instance);
documents[document1.FilePath] = TestDocumentState.Create(Workspace.Services, document1, onImportsChange: () => callCount++);
documents[document2.FilePath] = TestDocumentState.Create(Workspace.Services, document2, onImportsChange: () => callCount++);
documents[document3.FilePath] = TestDocumentState.Create(Workspace.Services, document3, onImportsChange: () => callCount++);
documents[document4.FilePath] = TestDocumentState.Create(Workspace.Services, document4, onImportsChange: () => callCount++);
documents[document5.FilePath] = TestDocumentState.Create(Workspace.Services, document5, onImportsChange: () => callCount++);
var importsToRelatedDocuments = ImmutableDictionary.CreateBuilder<string, ImmutableArray<string>>(FilePathComparer.Instance);
importsToRelatedDocuments.Add(
TestProjectData.SomeProjectImportFile.TargetPath,
ImmutableArray.Create(
TestProjectData.SomeProjectFile1.FilePath,
TestProjectData.SomeProjectFile2.FilePath,
TestProjectData.SomeProjectNestedFile3.FilePath,
TestProjectData.AnotherProjectNestedFile4.FilePath,
TestProjectData.AnotherProjectNestedImportFile.FilePath));
importsToRelatedDocuments.Add(
TestProjectData.SomeProjectNestedImportFile.TargetPath,
ImmutableArray.Create(
TestProjectData.SomeProjectNestedFile3.FilePath,
TestProjectData.AnotherProjectNestedFile4.FilePath));
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject);
original.Documents = documents.ToImmutable();
original.ImportsToRelatedDocuments = importsToRelatedDocuments.ToImmutable();
// Act
var state = original.WithRemovedHostDocument(document5);
// Assert
Assert.NotEqual(original.Version, state.Version);
Assert.Equal(2, callCount);
}
private class TestDocumentState : DocumentState
{
public static TestDocumentState Create(
@ -529,15 +896,27 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
Action onTextChange = null,
Action onTextLoaderChange = null,
Action onConfigurationChange = null,
Action onImportsChange = null,
Action onWorkspaceProjectChange = null)
{
return new TestDocumentState(services, hostDocument, null, null, loader, onTextChange, onTextLoaderChange, onConfigurationChange, onWorkspaceProjectChange);
return new TestDocumentState(
services,
hostDocument,
null,
null,
loader,
onTextChange,
onTextLoaderChange,
onConfigurationChange,
onImportsChange,
onWorkspaceProjectChange);
}
Action _onTextChange;
Action _onTextLoaderChange;
Action _onConfigurationChange;
Action _onWorkspaceProjectChange;
private readonly Action _onTextChange;
private readonly Action _onTextLoaderChange;
private readonly Action _onConfigurationChange;
private readonly Action _onImportsChange;
private readonly Action _onWorkspaceProjectChange;
private TestDocumentState(
HostWorkspaceServices services,
@ -548,12 +927,14 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
Action onTextChange,
Action onTextLoaderChange,
Action onConfigurationChange,
Action onImportsChange,
Action onWorkspaceProjectChange)
: base(services, hostDocument, text, version, loader)
{
_onTextChange = onTextChange;
_onTextLoaderChange = onTextLoaderChange;
_onConfigurationChange = onConfigurationChange;
_onImportsChange = onImportsChange;
_onWorkspaceProjectChange = onWorkspaceProjectChange;
}
@ -575,6 +956,12 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
return base.WithConfigurationChange();
}
public override DocumentState WithImportsChange()
{
_onImportsChange?.Invoke();
return base.WithImportsChange();
}
public override DocumentState WithWorkspaceProjectChange()
{
_onWorkspaceProjectChange?.Invoke();

Просмотреть файл

@ -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;
namespace Microsoft.AspNetCore.Razor.Language
{
public class TestImportProjectFeature : RazorProjectEngineFeatureBase, IImportProjectFeature
{
public IReadOnlyList<RazorProjectItem> GetImports(RazorProjectItem projectItem)
{
if (projectItem == null)
{
throw new ArgumentNullException(nameof(projectItem));
}
var imports = new List<RazorProjectItem>();
AddHierarchicalImports(projectItem, imports);
return imports;
}
private void AddHierarchicalImports(RazorProjectItem projectItem, List<RazorProjectItem> imports)
{
// We want items in descending order. FindHierarchicalItems returns items in ascending order.
var importProjectItems = ProjectEngine.FileSystem.FindHierarchicalItems(projectItem.FilePath, "_Imports.cshtml").Reverse();
imports.AddRange(importProjectItems);
}
}
}

Просмотреть файл

@ -0,0 +1,56 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
namespace Microsoft.CodeAnalysis.Razor
{
// Used to abstract away platform-specific file/directory path information.
//
// The System.IO.Path methods don't processes Windows paths in a Windows way
// on *nix (rightly so), so we need to use platform-specific paths.
//
// Target paths are always Windows style.
internal static class TestProjectData
{
static TestProjectData()
{
var baseDirectory = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "c:\\users\\example\\src" : "/home/example";
SomeProject = new HostProject(Path.Combine(baseDirectory, "SomeProject", "SomeProject.csproj"), RazorConfiguration.Default);
SomeProjectFile1 = new HostDocument(Path.Combine(baseDirectory, "SomeProject", "File1.cshtml"), "File1.cshtml");
SomeProjectFile2 = new HostDocument(Path.Combine(baseDirectory, "SomeProject", "File2.cshtml"), "File2.cshtml");
SomeProjectImportFile = new HostDocument(Path.Combine(baseDirectory, "SomeProject", "_Imports.cshtml"), "_Imports.cshtml");
SomeProjectNestedFile3 = new HostDocument(Path.Combine(baseDirectory, "SomeProject", "Nested", "File3.cshtml"), "Nested\\File1.cshtml");
SomeProjectNestedFile4 = new HostDocument(Path.Combine(baseDirectory, "SomeProject", "Nested", "File4.cshtml"), "Nested\\File2.cshtml");
SomeProjectNestedImportFile = new HostDocument(Path.Combine(baseDirectory, "SomeProject", "Nested", "_Imports.cshtml"), "Nested\\_Imports.cshtml");
AnotherProject = new HostProject(Path.Combine(baseDirectory, "AnotherProject", "AnotherProject.csproj"), RazorConfiguration.Default);
AnotherProjectFile1 = new HostDocument(Path.Combine(baseDirectory, "AnotherProject", "File1.cshtml"), "File1.cshtml");
AnotherProjectFile2 = new HostDocument(Path.Combine(baseDirectory, "AnotherProject", "File2.cshtml"), "File2.cshtml");
AnotherProjectImportFile = new HostDocument(Path.Combine(baseDirectory, "AnotherProject", "_Imports.cshtml"), "_Imports.cshtml");
AnotherProjectNestedFile3 = new HostDocument(Path.Combine(baseDirectory, "AnotherProject", "Nested", "File3.cshtml"), "Nested\\File1.cshtml");
AnotherProjectNestedFile4 = new HostDocument(Path.Combine(baseDirectory, "AnotherProject", "Nested", "File4.cshtml"), "Nested\\File2.cshtml");
AnotherProjectNestedImportFile = new HostDocument(Path.Combine(baseDirectory, "AnotherProject", "Nested", "_Imports.cshtml"), "Nested\\_Imports.cshtml");
}
public static readonly HostProject SomeProject;
public static readonly HostDocument SomeProjectFile1;
public static readonly HostDocument SomeProjectFile2;
public static readonly HostDocument SomeProjectImportFile;
public static readonly HostDocument SomeProjectNestedFile3;
public static readonly HostDocument SomeProjectNestedFile4;
public static readonly HostDocument SomeProjectNestedImportFile;
public static readonly HostProject AnotherProject;
public static readonly HostDocument AnotherProjectFile1;
public static readonly HostDocument AnotherProjectFile2;
public static readonly HostDocument AnotherProjectImportFile;
public static readonly HostDocument AnotherProjectNestedFile3;
public static readonly HostDocument AnotherProjectNestedFile4;
public static readonly HostDocument AnotherProjectNestedImportFile;
}
}

Просмотреть файл

@ -8,11 +8,13 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
internal class TestProjectSnapshotProjectEngineFactory : ProjectSnapshotProjectEngineFactory
{
public Action<RazorProjectEngineBuilder> Configure { get; set; }
public RazorProjectEngine Engine { get; set; }
public override RazorProjectEngine Create(ProjectSnapshot project, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
public override RazorProjectEngine Create(RazorConfiguration configuration, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
{
return Engine ?? RazorProjectEngine.Create(project.Configuration, fileSystem, configure);
return Engine ?? RazorProjectEngine.Create(configuration, fileSystem, configure ?? Configure);
}
public override IProjectEngineFactory FindFactory(ProjectSnapshot project)

Просмотреть файл

@ -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.Collections.Generic;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
namespace Microsoft.CodeAnalysis.Razor
{
public abstract class WorkspaceTestBase
{
private bool _initialized;
private HostServices _hostServices;
private Workspace _workspace;
protected WorkspaceTestBase()
{
}
protected HostServices HostServices
{
get
{
EnsureInitialized();
return _hostServices;
}
}
protected Workspace Workspace
{
get
{
EnsureInitialized();
return _workspace;
}
}
protected virtual void ConfigureWorkspaceServices(List<IWorkspaceService> services)
{
}
protected virtual void ConfigureLanguageServices(List<ILanguageService> services)
{
}
protected virtual void ConfigureWorkspace(AdhocWorkspace workspace)
{
}
protected virtual void ConfigureProjectEngine(RazorProjectEngineBuilder builder)
{
}
private void EnsureInitialized()
{
if (_initialized)
{
return;
}
var workspaceServices = new List<IWorkspaceService>()
{
new TestProjectSnapshotProjectEngineFactory()
{
Configure = ConfigureProjectEngine,
},
};
ConfigureWorkspaceServices(workspaceServices);
var languageServices = new List<ILanguageService>();
ConfigureLanguageServices(languageServices);
_hostServices = TestServices.Create(workspaceServices, languageServices);
_workspace = TestWorkspace.Create(_hostServices, ConfigureWorkspace);
_initialized = true;
}
}
}

Просмотреть файл

@ -0,0 +1,47 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
namespace Microsoft.CodeAnalysis.Razor
{
internal class SingleThreadedForegroundDispatcher : ForegroundDispatcher
{
public SingleThreadedForegroundDispatcher()
{
ForegroundScheduler = SynchronizationContext.Current == null ? new ThrowingTaskScheduler() : TaskScheduler.FromCurrentSynchronizationContext();
BackgroundScheduler = TaskScheduler.Default;
}
public override TaskScheduler ForegroundScheduler { get; }
public override TaskScheduler BackgroundScheduler { get; }
private Thread Thread { get; } = Thread.CurrentThread;
public override bool IsForegroundThread => Thread.CurrentThread == Thread;
private class ThrowingTaskScheduler : TaskScheduler
{
protected override IEnumerable<Task> GetScheduledTasks()
{
return Enumerable.Empty<Task>();
}
protected override void QueueTask(Task task)
{
throw new InvalidOperationException($"Use [{nameof(ForegroundFactAttribute)}]");
}
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
throw new InvalidOperationException($"Use [{nameof(ForegroundFactAttribute)}]");
}
}
}
}

Просмотреть файл

@ -1,11 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Razor;
namespace Xunit
@ -13,40 +8,5 @@ namespace Xunit
public abstract class ForegroundDispatcherTestBase
{
internal ForegroundDispatcher Dispatcher { get; } = new SingleThreadedForegroundDispatcher();
private class SingleThreadedForegroundDispatcher : ForegroundDispatcher
{
public SingleThreadedForegroundDispatcher()
{
ForegroundScheduler = SynchronizationContext.Current == null ? new ThrowingTaskScheduler() : TaskScheduler.FromCurrentSynchronizationContext();
BackgroundScheduler = TaskScheduler.Default;
}
public override TaskScheduler ForegroundScheduler { get; }
public override TaskScheduler BackgroundScheduler { get; }
private Thread Thread { get; } = Thread.CurrentThread;
public override bool IsForegroundThread => Thread.CurrentThread == Thread;
}
private class ThrowingTaskScheduler : TaskScheduler
{
protected override IEnumerable<Task> GetScheduledTasks()
{
return Enumerable.Empty<Task>();
}
protected override void QueueTask(Task task)
{
throw new InvalidOperationException($"Use [{nameof(ForegroundFactAttribute)}]");
}
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
throw new InvalidOperationException($"Use [{nameof(ForegroundFactAttribute)}]");
}
}
}
}

Просмотреть файл

@ -14,7 +14,8 @@ namespace Microsoft.VisualStudio.Editor.Razor.Documents
{
public DefaultImportDocumentManagerIntegrationTest()
{
ProjectPath = "C:\\path\\to\\project\\project.csproj";
ProjectPath = TestProjectData.SomeProject.FilePath;
DirectoryPath = Path.GetDirectoryName(ProjectPath);
FileSystem = RazorProjectFileSystem.Create(Path.GetDirectoryName(ProjectPath));
ProjectEngine = RazorProjectEngine.Create(FallbackRazorConfiguration.MVC_2_1, FileSystem, b =>
@ -24,7 +25,7 @@ namespace Microsoft.VisualStudio.Editor.Razor.Documents
});
}
private string FilePath { get; }
private string DirectoryPath { get; }
private string ProjectPath { get; }
@ -36,15 +37,15 @@ namespace Microsoft.VisualStudio.Editor.Razor.Documents
public void Changed_TrackerChanged_ResultsInChangedHavingCorrectArgs()
{
// Arrange
var testImportsPath = "C:\\path\\to\\project\\_ViewImports.cshtml";
var testImportsPath = Path.Combine(DirectoryPath, "_ViewImports.cshtml");
var tracker = Mock.Of<VisualStudioDocumentTracker>(
t => t.FilePath == "C:\\path\\to\\project\\Views\\Home\\file.cshtml" &&
t => t.FilePath == Path.Combine(DirectoryPath, "Views", "Home", "_ViewImports.cshtml") &&
t.ProjectPath == ProjectPath &&
t.ProjectSnapshot == Mock.Of<ProjectSnapshot>(p => p.GetProjectEngine() == ProjectEngine));
var anotherTracker = Mock.Of<VisualStudioDocumentTracker>(
t => t.FilePath == "C:\\path\\to\\project\\anotherFile.cshtml" &&
t => t.FilePath == Path.Combine(DirectoryPath, "anotherFile.cshtml") &&
t.ProjectPath == ProjectPath &&
t.ProjectSnapshot == Mock.Of<ProjectSnapshot>(p => p.GetProjectEngine() == ProjectEngine));
@ -56,12 +57,12 @@ namespace Microsoft.VisualStudio.Editor.Razor.Documents
fileChangeTrackerFactory
.Setup(f => f.Create(testImportsPath))
.Returns(fileChangeTracker.Object);
fileChangeTrackerFactory
.Setup(f => f.Create("C:\\path\\to\\project\\Views\\_ViewImports.cshtml"))
.Setup(f => f.Create(Path.Combine(DirectoryPath, "Views", "_ViewImports.cshtml")))
.Returns(Mock.Of<FileChangeTracker>());
fileChangeTrackerFactory
.Setup(f => f.Create("C:\\path\\to\\project\\Views\\Home\\_ViewImports.cshtml"))
.Setup(f => f.Create(Path.Combine(DirectoryPath, "Views", "Home", "_ViewImports.cshtml")))
.Returns(Mock.Of<FileChangeTracker>());
var called = false;
@ -76,8 +77,8 @@ namespace Microsoft.VisualStudio.Editor.Razor.Documents
Assert.Equal(FileChangeKind.Changed, args.Kind);
Assert.Collection(
args.AssociatedDocuments,
f => Assert.Equal("C:\\path\\to\\project\\Views\\Home\\file.cshtml", f),
f => Assert.Equal("C:\\path\\to\\project\\anotherFile.cshtml", f));
f => Assert.Equal(Path.Combine(DirectoryPath, "Views", "Home", "_ViewImports.cshtml"), f),
f => Assert.Equal(Path.Combine(DirectoryPath, "anotherFile.cshtml"), f));
};
// Act

Просмотреть файл

@ -16,7 +16,8 @@ namespace Microsoft.VisualStudio.Editor.Razor
{
public DefaultImportDocumentManagerTest()
{
ProjectPath = "C:\\path\\to\\project\\project.csproj";
ProjectPath = TestProjectData.SomeProject.FilePath;
DirectoryPath = Path.GetDirectoryName(ProjectPath);
FileSystem = RazorProjectFileSystem.Create(Path.GetDirectoryName(ProjectPath));
ProjectEngine = RazorProjectEngine.Create(FallbackRazorConfiguration.MVC_2_1, FileSystem, b =>
@ -26,10 +27,10 @@ namespace Microsoft.VisualStudio.Editor.Razor
});
}
private string FilePath { get; }
private string ProjectPath { get; }
private string DirectoryPath { get; }
private RazorProjectFileSystem FileSystem { get; }
private RazorProjectEngine ProjectEngine { get; }
@ -39,17 +40,17 @@ namespace Microsoft.VisualStudio.Editor.Razor
{
// Arrange
var tracker = Mock.Of<VisualStudioDocumentTracker>(
t => t.FilePath == "C:\\path\\to\\project\\Views\\Home\\file.cshtml" &&
t => t.FilePath == Path.Combine(DirectoryPath, "Views", "Home", "file.cshtml") &&
t.ProjectPath == ProjectPath &&
t.ProjectSnapshot == Mock.Of<ProjectSnapshot>(p => p.GetProjectEngine() == ProjectEngine));
var fileChangeTrackerFactory = new Mock<FileChangeTrackerFactory>();
var fileChangeTracker1 = new Mock<FileChangeTracker>();
fileChangeTracker1
.Setup(f => f.StartListening())
.Verifiable();
fileChangeTrackerFactory
.Setup(f => f.Create("C:\\path\\to\\project\\Views\\Home\\_ViewImports.cshtml"))
.Setup(f => f.Create(Path.Combine(DirectoryPath, "Views", "Home", "_ViewImports.cshtml")))
.Returns(fileChangeTracker1.Object)
.Verifiable();
var fileChangeTracker2 = new Mock<FileChangeTracker>();
@ -57,13 +58,13 @@ namespace Microsoft.VisualStudio.Editor.Razor
.Setup(f => f.StartListening())
.Verifiable();
fileChangeTrackerFactory
.Setup(f => f.Create("C:\\path\\to\\project\\Views\\_ViewImports.cshtml"))
.Setup(f => f.Create(Path.Combine(DirectoryPath, "Views", "_ViewImports.cshtml")))
.Returns(fileChangeTracker2.Object)
.Verifiable();
var fileChangeTracker3 = new Mock<FileChangeTracker>();
fileChangeTracker3.Setup(f => f.StartListening()).Verifiable();
fileChangeTrackerFactory
.Setup(f => f.Create("C:\\path\\to\\project\\_ViewImports.cshtml"))
.Setup(f => f.Create(Path.Combine(DirectoryPath, "_ViewImports.cshtml")))
.Returns(fileChangeTracker3.Object)
.Verifiable();
@ -84,12 +85,12 @@ namespace Microsoft.VisualStudio.Editor.Razor
{
// Arrange
var tracker = Mock.Of<VisualStudioDocumentTracker>(
t => t.FilePath == "C:\\path\\to\\project\\file.cshtml" &&
t => t.FilePath == Path.Combine(DirectoryPath, "file.cshtml") &&
t.ProjectPath == ProjectPath &&
t.ProjectSnapshot == Mock.Of<ProjectSnapshot>(p => p.GetProjectEngine() == ProjectEngine));
var anotherTracker = Mock.Of<VisualStudioDocumentTracker>(
t => t.FilePath == "C:\\path\\to\\project\\anotherFile.cshtml" &&
t => t.FilePath == Path.Combine(DirectoryPath, "anotherFile.cshtml") &&
t.ProjectPath == ProjectPath &&
t.ProjectSnapshot == Mock.Of<ProjectSnapshot>(p => p.GetProjectEngine() == ProjectEngine));
@ -115,7 +116,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
{
// Arrange
var tracker = Mock.Of<VisualStudioDocumentTracker>(
t => t.FilePath == "C:\\path\\to\\project\\file.cshtml" &&
t => t.FilePath == Path.Combine(DirectoryPath, "file.cshtml") &&
t.ProjectPath == ProjectPath &&
t.ProjectSnapshot == Mock.Of<ProjectSnapshot>(p => p.GetProjectEngine() == ProjectEngine));
@ -125,7 +126,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
.Setup(f => f.StopListening())
.Verifiable();
fileChangeTrackerFactory
.Setup(f => f.Create("C:\\path\\to\\project\\_ViewImports.cshtml"))
.Setup(f => f.Create(Path.Combine(DirectoryPath, "_ViewImports.cshtml")))
.Returns(fileChangeTracker.Object)
.Verifiable();
@ -145,12 +146,12 @@ namespace Microsoft.VisualStudio.Editor.Razor
{
// Arrange
var tracker = Mock.Of<VisualStudioDocumentTracker>(
t => t.FilePath == "C:\\path\\to\\project\\file.cshtml" &&
t => t.FilePath == Path.Combine(DirectoryPath, "file.cshtml") &&
t.ProjectPath == ProjectPath &&
t.ProjectSnapshot == Mock.Of<ProjectSnapshot>(p => p.GetProjectEngine() == ProjectEngine));
var anotherTracker = Mock.Of<VisualStudioDocumentTracker>(
t => t.FilePath == "C:\\path\\to\\project\\anotherFile.cshtml" &&
t => t.FilePath == Path.Combine(DirectoryPath, "anotherFile.cshtml") &&
t.ProjectPath == ProjectPath &&
t.ProjectSnapshot == Mock.Of<ProjectSnapshot>(p => p.GetProjectEngine() == ProjectEngine));

Просмотреть файл

@ -26,8 +26,8 @@ namespace Microsoft.VisualStudio.Editor.Razor
RazorCoreContentType = Mock.Of<IContentType>(c => c.IsOfType(RazorLanguage.ContentType) == true);
TextBuffer = Mock.Of<ITextBuffer>(b => b.ContentType == RazorCoreContentType);
FilePath = "C:/Some/Path/TestDocumentTracker.cshtml";
ProjectPath = "C:/Some/Path/TestProject.csproj";
FilePath = TestProjectData.SomeProjectFile1.FilePath;
ProjectPath = TestProjectData.SomeProject.FilePath;
ImportDocumentManager = Mock.Of<ImportDocumentManager>();
WorkspaceEditorSettings = new DefaultWorkspaceEditorSettings(Mock.Of<ForegroundDispatcher>(), Mock.Of<EditorSettingsManager>());
@ -56,7 +56,8 @@ namespace Microsoft.VisualStudio.Editor.Razor
ProjectManager = new TestProjectSnapshotManager(Dispatcher, Workspace) { AllowNotifyListeners = true };
HostProject = new HostProject(ProjectPath, FallbackRazorConfiguration.MVC_2_1);
OtherHostProject = new HostProject(ProjectPath, FallbackRazorConfiguration.MVC_2_0);
UpdatedHostProject = new HostProject(ProjectPath, FallbackRazorConfiguration.MVC_2_0);
OtherHostProject = new HostProject(TestProjectData.AnotherProject.FilePath, FallbackRazorConfiguration.MVC_2_0);
DocumentTracker = new DefaultVisualStudioDocumentTracker(
Dispatcher,
@ -79,6 +80,8 @@ namespace Microsoft.VisualStudio.Editor.Razor
private HostProject HostProject { get; }
private HostProject UpdatedHostProject { get; }
private HostProject OtherHostProject { get; }
private Project WorkspaceProject { get; set; }
@ -191,7 +194,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
ProjectManager.HostProjectAdded(HostProject);
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
var e = new ProjectChangeEventArgs(ProjectPath, ProjectChangeKind.ProjectAdded);
var e = new ProjectChangeEventArgs(null, ProjectManager.GetLoadedProject(HostProject.FilePath), ProjectChangeKind.ProjectAdded);
var called = false;
DocumentTracker.ContextChanged += (sender, args) =>
@ -215,8 +218,8 @@ namespace Microsoft.VisualStudio.Editor.Razor
// Arrange
ProjectManager.HostProjectAdded(HostProject);
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
var e = new ProjectChangeEventArgs(ProjectPath, ProjectChangeKind.ProjectChanged);
var e = new ProjectChangeEventArgs(null, ProjectManager.GetLoadedProject(HostProject.FilePath), ProjectChangeKind.ProjectChanged);
var called = false;
DocumentTracker.ContextChanged += (sender, args) =>
@ -238,7 +241,13 @@ namespace Microsoft.VisualStudio.Editor.Razor
public void ProjectManager_Changed_ProjectRemoved_TriggersContextChanged_WithEphemeralProject()
{
// Arrange
var e = new ProjectChangeEventArgs(ProjectPath, ProjectChangeKind.ProjectRemoved);
ProjectManager.HostProjectAdded(HostProject);
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
var project = ProjectManager.GetLoadedProject(HostProject.FilePath);
ProjectManager.HostProjectRemoved(HostProject);
var e = new ProjectChangeEventArgs(project, null, ProjectChangeKind.ProjectRemoved);
var called = false;
DocumentTracker.ContextChanged += (sender, args) =>
@ -260,7 +269,9 @@ namespace Microsoft.VisualStudio.Editor.Razor
public void ProjectManager_Changed_IgnoresUnknownProject()
{
// Arrange
var e = new ProjectChangeEventArgs("c:/OtherPath/OtherProject.csproj", ProjectChangeKind.ProjectChanged);
ProjectManager.HostProjectAdded(OtherHostProject);
var e = new ProjectChangeEventArgs(null, ProjectManager.GetLoadedProject(OtherHostProject.FilePath), ProjectChangeKind.ProjectChanged);
var called = false;
DocumentTracker.ContextChanged += (sender, args) =>
@ -488,13 +499,13 @@ namespace Microsoft.VisualStudio.Editor.Razor
DocumentTracker.ContextChanged += (sender, e) => { args.Add(e); };
// Act
ProjectManager.HostProjectChanged(OtherHostProject);
ProjectManager.HostProjectChanged(UpdatedHostProject);
await DocumentTracker.PendingTagHelperTask;
// Assert
var snapshot = Assert.IsType<DefaultProjectSnapshot>(DocumentTracker.ProjectSnapshot);
Assert.Same(OtherHostProject, snapshot.HostProject);
Assert.Same(UpdatedHostProject, snapshot.HostProject);
Assert.Collection(
args,

Просмотреть файл

@ -24,7 +24,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
var engine = RazorProjectEngine.Create(RazorConfiguration.Default, RazorProjectFileSystem.Empty);
ProjectEngineFactory = Mock.Of<ProjectSnapshotProjectEngineFactory>(
f => f.Create(
It.IsAny<ProjectSnapshot>(),
It.IsAny<RazorConfiguration>(),
It.IsAny<RazorProjectFileSystem>(),
It.IsAny<Action<RazorProjectEngineBuilder>>()) == engine);
}

Просмотреть файл

@ -21,13 +21,13 @@ namespace Microsoft.VisualStudio.Editor.Razor.Documents
private TestEditorDocumentManager Manager { get; }
public string Project1 => "c:\\Project1";
public string Project1 => TestProjectData.SomeProject.FilePath;
public string Project2 => "c:\\Project2";
public string Project2 => TestProjectData.AnotherProject.FilePath;
public string File1 => "c:\\Project1\\File1.cshtml";
public string File1 => TestProjectData.SomeProjectFile1.FilePath;
public string File2 => "c:\\Project2\\File2.cshtml";
public string File2 => TestProjectData.AnotherProjectFile2.FilePath;
public TestTextBuffer TextBuffer => new TestTextBuffer(new StringTextSnapshot("HI"));
@ -113,8 +113,8 @@ namespace Microsoft.VisualStudio.Editor.Razor.Documents
// Assert
Assert.Collection(
documents.OrderBy(d => d.ProjectFilePath),
d => Assert.Same(document1, d),
d => Assert.Same(document2, d));
d => Assert.Same(document2, d),
d => Assert.Same(document1, d));
}
[ForegroundFact]
@ -147,8 +147,8 @@ namespace Microsoft.VisualStudio.Editor.Razor.Documents
// Assert
Assert.Collection(
Manager.Opened.OrderBy(d => d.ProjectFilePath),
d => Assert.Same(document1, d),
d => Assert.Same(document2, d));
d => Assert.Same(document2, d),
d => Assert.Same(document1, d));
}
[ForegroundFact]
@ -165,8 +165,8 @@ namespace Microsoft.VisualStudio.Editor.Razor.Documents
// Assert
Assert.Collection(
Manager.Closed.OrderBy(d => d.ProjectFilePath),
d => Assert.Same(document1, d),
d => Assert.Same(document2, d));
d => Assert.Same(document2, d),
d => Assert.Same(document1, d));
}
private class TestEditorDocumentManager : EditorDocumentManagerBase

Просмотреть файл

@ -17,8 +17,8 @@ namespace Microsoft.VisualStudio.Editor.Razor.Documents
{
public EditorDocumentManagerListenerTest()
{
ProjectFilePath = "C:\\project1\\project.csproj";
DocumentFilePath = "c:\\project1\\file1.cshtml";
ProjectFilePath = TestProjectData.SomeProject.FilePath;
DocumentFilePath = TestProjectData.SomeProjectFile1.FilePath;
TextLoader = TextLoader.From(TextAndVersion.Create(SourceText.From("FILE"), VersionStamp.Default));
FileChangeTracker = new DefaultFileChangeTracker(DocumentFilePath);
@ -58,8 +58,10 @@ namespace Microsoft.VisualStudio.Editor.Razor.Documents
var listener = new EditorDocumentManagerListener(editorDocumentManger.Object, changedOnDisk, changedInEditor, opened, closed);
var project = Mock.Of<ProjectSnapshot>(p => p.FilePath == "/Path/to/project.csproj");
// Act & Assert
listener.ProjectManager_Changed(null, new ProjectChangeEventArgs("/Path/to/project.csproj", ProjectChangeKind.DocumentAdded));
listener.ProjectManager_Changed(null, new ProjectChangeEventArgs(project, project, ProjectChangeKind.DocumentAdded));
}
[Fact]
@ -76,8 +78,10 @@ namespace Microsoft.VisualStudio.Editor.Razor.Documents
var listener = new EditorDocumentManagerListener(editorDocumentManger.Object, onChangedOnDisk: null, onChangedInEditor: null, onOpened: opened, onClosed: null);
var project = Mock.Of<ProjectSnapshot>(p => p.FilePath == "/Path/to/project.csproj");
// Act
listener.ProjectManager_Changed(null, new ProjectChangeEventArgs("/Path/to/project.csproj", ProjectChangeKind.DocumentAdded));
listener.ProjectManager_Changed(null, new ProjectChangeEventArgs(project, project, ProjectChangeKind.DocumentAdded));
// Assert
Assert.True(called);

Просмотреть файл

@ -3,6 +3,7 @@
using System;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Test;
using Microsoft.VisualStudio.Text;
@ -16,8 +17,8 @@ namespace Microsoft.VisualStudio.Editor.Razor.Documents
public EditorDocumentTest()
{
DocumentManager = Mock.Of<EditorDocumentManager>();
ProjectFilePath = "C:\\project1\\project.csproj";
DocumentFilePath = "c:\\project1\\file1.cshtml";
ProjectFilePath = TestProjectData.SomeProject.FilePath;
DocumentFilePath = TestProjectData.SomeProjectFile1.FilePath;
TextLoader = TextLoader.From(TextAndVersion.Create(SourceText.From("FILE"), VersionStamp.Default));
FileChangeTracker = new DefaultFileChangeTracker(DocumentFilePath);

Просмотреть файл

@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.Razor;
namespace Xunit
{
public abstract class ForegroundDispatcherWorkspaceTestBase : WorkspaceTestBase
{
internal ForegroundDispatcher Dispatcher { get; } = new SingleThreadedForegroundDispatcher();
}
}

Просмотреть файл

@ -5,6 +5,8 @@ using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Host;
using Moq;
using Xunit;
@ -12,20 +14,18 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
// These tests are really integration tests. There isn't a good way to unit test this functionality since
// the only thing in here is threading.
public class BackgroundDocumentGeneratorTest : ForegroundDispatcherTestBase
public class BackgroundDocumentGeneratorTest : ForegroundDispatcherWorkspaceTestBase
{
public BackgroundDocumentGeneratorTest()
{
Documents = new HostDocument[]
{
new HostDocument("c:\\Test1\\Index.cshtml", "Index.cshtml"),
new HostDocument("c:\\Test1\\Components\\Counter.cshtml", "Components\\Counter.cshtml"),
TestProjectData.SomeProjectFile1,
TestProjectData.AnotherProjectFile1,
};
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();
HostProject1 = new HostProject(TestProjectData.SomeProject.FilePath, FallbackRazorConfiguration.MVC_1_0);
HostProject2 = new HostProject(TestProjectData.AnotherProject.FilePath, FallbackRazorConfiguration.MVC_1_0);
var projectId1 = ProjectId.CreateNewId("Test1");
var projectId2 = ProjectId.CreateNewId("Test2");
@ -37,14 +37,14 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
"Test1",
"Test1",
LanguageNames.CSharp,
"c:\\Test1\\Test1.csproj"))
TestProjectData.SomeProject.FilePath))
.AddProject(ProjectInfo.Create(
projectId2,
VersionStamp.Default,
"Test2",
"Test2",
LanguageNames.CSharp,
"c:\\Test2\\Test2.csproj")); ;
TestProjectData.AnotherProject.FilePath)); ;
WorkspaceProject1 = solution.GetProject(projectId1);
WorkspaceProject2 = solution.GetProject(projectId2);
@ -60,7 +60,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
private Project WorkspaceProject2 { get; }
private Workspace Workspace { get; }
protected override void ConfigureProjectEngine(RazorProjectEngineBuilder builder)
{
builder.Features.Remove(builder.Features.OfType<IImportProjectFeature>().Single());
builder.Features.Add(new TestImportProjectFeature());
}
[ForegroundFact]
public async Task Queue_ProcessesNotifications_AndGoesBackToSleep()

Просмотреть файл

@ -23,7 +23,7 @@ namespace Microsoft.VisualStudio.Editor.Razor.Documents
.Setup(f => f.AdviseFileChange(It.IsAny<string>(), It.IsAny<uint>(), It.IsAny<IVsFileChangeEvents>(), out cookie))
.Returns(VSConstants.S_OK)
.Verifiable();
var tracker = new VisualStudioFileChangeTracker("C:/_ViewImports.cshtml", Dispatcher, ErrorReporter, fileChangeService.Object);
var tracker = new VisualStudioFileChangeTracker(TestProjectData.SomeProjectImportFile.FilePath, Dispatcher, ErrorReporter, fileChangeService.Object);
// Act
tracker.StartListening();
@ -43,7 +43,7 @@ namespace Microsoft.VisualStudio.Editor.Razor.Documents
.Setup(f => f.AdviseFileChange(It.IsAny<string>(), It.IsAny<uint>(), It.IsAny<IVsFileChangeEvents>(), out cookie))
.Returns(VSConstants.S_OK)
.Callback(() => callCount++);
var tracker = new VisualStudioFileChangeTracker("C:/_ViewImports.cshtml", Dispatcher, ErrorReporter, fileChangeService.Object);
var tracker = new VisualStudioFileChangeTracker(TestProjectData.SomeProjectImportFile.FilePath, Dispatcher, ErrorReporter, fileChangeService.Object);
tracker.StartListening();
// Act
@ -67,7 +67,7 @@ namespace Microsoft.VisualStudio.Editor.Razor.Documents
.Setup(f => f.UnadviseFileChange(cookie))
.Returns(VSConstants.S_OK)
.Verifiable();
var tracker = new VisualStudioFileChangeTracker("C:/_ViewImports.cshtml", Dispatcher, ErrorReporter, fileChangeService.Object);
var tracker = new VisualStudioFileChangeTracker(TestProjectData.SomeProjectImportFile.FilePath, Dispatcher, ErrorReporter, fileChangeService.Object);
tracker.StartListening(); // Start listening for changes.
// Act
@ -86,7 +86,7 @@ namespace Microsoft.VisualStudio.Editor.Razor.Documents
fileChangeService
.Setup(f => f.UnadviseFileChange(cookie))
.Throws(new InvalidOperationException());
var tracker = new VisualStudioFileChangeTracker("C:/_ViewImports.cshtml", Dispatcher, ErrorReporter, fileChangeService.Object);
var tracker = new VisualStudioFileChangeTracker(TestProjectData.SomeProjectImportFile.FilePath, Dispatcher, ErrorReporter, fileChangeService.Object);
// Act & Assert
tracker.StopListening();
@ -100,7 +100,7 @@ namespace Microsoft.VisualStudio.Editor.Razor.Documents
public void FilesChanged_WithSpecificFlags_InvokesChangedHandler_WithExpectedArguments(uint fileChangeFlag, int expectedKind)
{
// Arrange
var filePath = "C:\\path\\to\\project\\_ViewImports.cshtml";
var filePath = TestProjectData.SomeProjectImportFile.FilePath;
uint cookie;
var fileChangeService = new Mock<IVsFileChangeEx>();
fileChangeService

Просмотреть файл

@ -14,6 +14,9 @@
<Compile Include="..\Microsoft.CodeAnalysis.Razor.Workspaces.Test\Shared\**\*.cs">
<Link>Shared\%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="..\Microsoft.VisualStudio.Editor.Razor.Test\Shared\**\*.cs">
<Link>Shared\%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -13,35 +13,24 @@ using Xunit;
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
public class DefaultProjectSnapshotManagerTest : ForegroundDispatcherTestBase
public class DefaultProjectSnapshotManagerTest : ForegroundDispatcherWorkspaceTestBase
{
public DefaultProjectSnapshotManagerTest()
{
TagHelperResolver = new TestTagHelperResolver();
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"),
TestProjectData.SomeProjectFile1,
TestProjectData.SomeProjectFile2,
// linked file
new HostDocument("c:\\SomeOtherProject\\Index.cshtml", "Pages\\Index.cshtml"),
TestProjectData.AnotherProjectNestedFile3,
};
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);
HostProject = new HostProject(TestProjectData.SomeProject.FilePath, FallbackRazorConfiguration.MVC_2_0);
HostProjectWithConfigurationChange = new HostProject(TestProjectData.SomeProject.FilePath, FallbackRazorConfiguration.MVC_1_0);
ProjectManager = new TestProjectSnapshotManager(Dispatcher, Enumerable.Empty<ProjectSnapshotChangeTrigger>(), Workspace);
var projectId = ProjectId.CreateNewId("Test");
@ -51,7 +40,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
"Test",
"Test",
LanguageNames.CSharp,
"c:\\MyProject\\Test.csproj"));
TestProjectData.SomeProject.FilePath));
WorkspaceProject = solution.GetProject(projectId);
var vbProjectId = ProjectId.CreateNewId("VB");
@ -81,7 +70,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
"Test (Different TFM)",
"Test",
LanguageNames.CSharp,
"c:\\MyProject\\Test.csproj"));
TestProjectData.SomeProject.FilePath));
WorkspaceProjectWithDifferentTfm = solution.GetProject(projectIdWithDifferentTfm);
SomeTagHelpers = TagHelperResolver.TagHelpers;
@ -108,14 +97,15 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
private TestProjectSnapshotManager ProjectManager { get; }
private HostServices HostServices { get; }
private Workspace Workspace { get; }
private SourceText SourceText { get; }
private IList<TagHelperDescriptor> SomeTagHelpers { get; }
protected override void ConfigureLanguageServices(List<ILanguageService> services)
{
services.Add(TagHelperResolver);
}
[ForegroundFact]
public void DocumentAdded_AddsDocument()
{
@ -129,7 +119,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// Assert
var snapshot = ProjectManager.GetSnapshot(HostProject);
Assert.Collection(snapshot.DocumentFilePaths, d => Assert.Equal(Documents[0].FilePath, d));
Assert.Collection(snapshot.DocumentFilePaths.OrderBy(f => f), d => Assert.Equal(Documents[0].FilePath, d));
Assert.Equal(ProjectChangeKind.DocumentAdded, ProjectManager.ListenersNotifiedOf);
}
@ -148,7 +138,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// Assert
var snapshot = ProjectManager.GetSnapshot(HostProject);
Assert.Collection(snapshot.DocumentFilePaths, d => Assert.Equal(Documents[0].FilePath, d));
Assert.Collection(snapshot.DocumentFilePaths.OrderBy(f => f), d => Assert.Equal(Documents[0].FilePath, d));
Assert.Null(ProjectManager.ListenersNotifiedOf);
}
@ -262,9 +252,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// Assert
var snapshot = ProjectManager.GetSnapshot(HostProject);
Assert.Collection(
snapshot.DocumentFilePaths,
d => Assert.Equal(Documents[0].FilePath, d),
d => Assert.Equal(Documents[2].FilePath, d));
snapshot.DocumentFilePaths.OrderBy(f => f),
d => Assert.Equal(Documents[2].FilePath, d),
d => Assert.Equal(Documents[0].FilePath, d));
Assert.Equal(ProjectChangeKind.DocumentRemoved, ProjectManager.ListenersNotifiedOf);
}

Просмотреть файл

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
@ -15,11 +16,10 @@ using ItemCollection = Microsoft.VisualStudio.ProjectSystem.ItemCollection;
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
public class DefaultRazorProjectHostTest : ForegroundDispatcherTestBase
public class DefaultRazorProjectHostTest : ForegroundDispatcherWorkspaceTestBase
{
public DefaultRazorProjectHostTest()
{
Workspace = new AdhocWorkspace();
ProjectManager = new TestProjectSnapshotManager(Dispatcher, Workspace);
ConfigurationItems = new ItemCollection(Rules.RazorConfiguration.SchemaName);
@ -38,8 +38,6 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
private TestProjectSnapshotManager ProjectManager { get; }
private Workspace Workspace { get; }
[Fact]
public void TryGetDefaultConfiguration_FailsIfNoRule()
{
@ -612,7 +610,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
public async Task DefaultRazorProjectHost_ForegroundThread_CreateAndDispose_Succeeds()
{
// Arrange
var services = new TestProjectSystemServices("c:\\MyProject\\Test.csproj");
var services = new TestProjectSystemServices(TestProjectData.SomeProject.FilePath);
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
// Act & Assert
@ -627,7 +625,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
public async Task DefaultRazorProjectHost_BackgroundThread_CreateAndDispose_Succeeds()
{
// Arrange
var services = new TestProjectSystemServices("c:\\MyProject\\Test.csproj");
var services = new TestProjectSystemServices(TestProjectData.SomeProject.FilePath);
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
// Act & Assert
@ -646,7 +644,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
};
var services = new TestProjectSystemServices("Test.csproj");
var services = new TestProjectSystemServices(TestProjectData.SomeProject.FilePath);
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
// Act & Assert
@ -670,8 +668,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
ExtensionItems.Item("MVC-2.1");
ExtensionItems.Item("Another-Thing");
DocumentItems.Item("File.cshtml");
DocumentItems.Property("File.cshtml", Rules.RazorGenerateWithTargetPath.TargetPathProperty, "File.cshtml");
DocumentItems.Item(Path.GetFileName(TestProjectData.SomeProjectFile1.FilePath));
DocumentItems.Property(Path.GetFileName(TestProjectData.SomeProjectFile1.FilePath), Rules.RazorGenerateWithTargetPath.TargetPathProperty, TestProjectData.SomeProjectFile1.TargetPath);
var changes = new TestProjectChangeDescription[]
{
@ -681,7 +679,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
DocumentItems.ToChange(),
};
var services = new TestProjectSystemServices("c:\\MyProject\\Test.csproj");
var services = new TestProjectSystemServices(TestProjectData.SomeProject.FilePath);
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
@ -693,7 +691,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// Assert
var snapshot = Assert.Single(ProjectManager.Projects);
Assert.Equal("c:\\MyProject\\Test.csproj", snapshot.FilePath);
Assert.Equal(TestProjectData.SomeProject.FilePath, snapshot.FilePath);
Assert.Equal(RazorLanguageVersion.Version_2_1, snapshot.Configuration.LanguageVersion);
Assert.Equal("MVC-2.1", snapshot.Configuration.ConfigurationName);
@ -707,8 +705,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
d =>
{
var document = snapshot.GetDocument(d);
Assert.Equal("c:\\MyProject\\File.cshtml", document.FilePath);
Assert.Equal("File.cshtml", document.TargetPath);
Assert.Equal(TestProjectData.SomeProjectFile1.FilePath, document.FilePath);
Assert.Equal(TestProjectData.SomeProjectFile1.TargetPath, document.TargetPath);
});
await Task.Run(async () => await host.DisposeAsync());
@ -726,7 +724,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
ExtensionItems.Item("TestExtension");
DocumentItems.Item("File.cshtml");
DocumentItems.Item(Path.GetFileName(TestProjectData.SomeProjectFile1.FilePath));
var changes = new TestProjectChangeDescription[]
{
@ -736,7 +734,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
DocumentItems.ToChange(),
};
var services = new TestProjectSystemServices("c:\\MyProject\\Test.csproj");
var services = new TestProjectSystemServices(TestProjectData.SomeProject.FilePath);
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
@ -766,8 +764,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
ExtensionItems.Item("MVC-2.1");
ExtensionItems.Item("Another-Thing");
DocumentItems.Item("File.cshtml");
DocumentItems.Property("File.cshtml", Rules.RazorGenerateWithTargetPath.TargetPathProperty, "File.cshtml");
DocumentItems.Item(Path.GetFileName(TestProjectData.SomeProjectFile1.FilePath));
DocumentItems.Property(Path.GetFileName(TestProjectData.SomeProjectFile1.FilePath), Rules.RazorGenerateWithTargetPath.TargetPathProperty, TestProjectData.SomeProjectFile1.TargetPath);
var changes = new TestProjectChangeDescription[]
{
@ -777,7 +775,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
DocumentItems.ToChange(),
};
var services = new TestProjectSystemServices("c:\\MyProject\\Test.csproj");
var services = new TestProjectSystemServices(TestProjectData.SomeProject.FilePath);
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
@ -789,7 +787,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// Assert - 1
var snapshot = Assert.Single(ProjectManager.Projects);
Assert.Equal("c:\\MyProject\\Test.csproj", snapshot.FilePath);
Assert.Equal(TestProjectData.SomeProject.FilePath, snapshot.FilePath);
Assert.Equal(RazorLanguageVersion.Version_2_1, snapshot.Configuration.LanguageVersion);
Assert.Equal("MVC-2.1", snapshot.Configuration.ConfigurationName);
@ -803,8 +801,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
d =>
{
var document = snapshot.GetDocument(d);
Assert.Equal("c:\\MyProject\\File.cshtml", document.FilePath);
Assert.Equal("File.cshtml", document.TargetPath);
Assert.Equal(TestProjectData.SomeProjectFile1.FilePath, document.FilePath);
Assert.Equal(TestProjectData.SomeProjectFile1.TargetPath, document.TargetPath);
});
// Act - 2
@ -813,9 +811,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
ConfigurationItems.RemoveItem("MVC-2.1");
ConfigurationItems.Item("MVC-2.0", new Dictionary<string, string>() { { "Extensions", "MVC-2.0;Another-Thing" }, });
ExtensionItems.Item("MVC-2.0");
DocumentItems.Item("c:\\AnotherProject\\AnotherFile.cshtml", new Dictionary<string, string>()
DocumentItems.Item(TestProjectData.AnotherProjectNestedFile3.FilePath, new Dictionary<string, string>()
{
{ Rules.RazorGenerateWithTargetPath.TargetPathProperty, "Pages\\AnotherFile.cshtml" },
{ Rules.RazorGenerateWithTargetPath.TargetPathProperty, TestProjectData.AnotherProjectNestedFile3.TargetPath },
});
changes = new TestProjectChangeDescription[]
@ -830,7 +828,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// Assert - 2
snapshot = Assert.Single(ProjectManager.Projects);
Assert.Equal("c:\\MyProject\\Test.csproj", snapshot.FilePath);
Assert.Equal(TestProjectData.SomeProject.FilePath, snapshot.FilePath);
Assert.Equal(RazorLanguageVersion.Version_2_0, snapshot.Configuration.LanguageVersion);
Assert.Equal("MVC-2.0", snapshot.Configuration.ConfigurationName);
@ -844,14 +842,14 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
d =>
{
var document = snapshot.GetDocument(d);
Assert.Equal("c:\\AnotherProject\\AnotherFile.cshtml", document.FilePath);
Assert.Equal("Pages\\AnotherFile.cshtml", document.TargetPath);
Assert.Equal(TestProjectData.AnotherProjectNestedFile3.FilePath, document.FilePath);
Assert.Equal(TestProjectData.AnotherProjectNestedFile3.TargetPath, document.TargetPath);
},
d =>
{
var document = snapshot.GetDocument(d);
Assert.Equal("c:\\MyProject\\File.cshtml", document.FilePath);
Assert.Equal("File.cshtml", document.TargetPath);
Assert.Equal(TestProjectData.SomeProjectFile1.FilePath, document.FilePath);
Assert.Equal(TestProjectData.SomeProjectFile1.TargetPath, document.TargetPath);
});
await Task.Run(async () => await host.DisposeAsync());
@ -871,8 +869,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
ExtensionItems.Item("MVC-2.1");
ExtensionItems.Item("Another-Thing");
DocumentItems.Item("File.cshtml");
DocumentItems.Property("File.cshtml", Rules.RazorGenerateWithTargetPath.TargetPathProperty, "File.cshtml");
DocumentItems.Item(Path.GetFileName(TestProjectData.SomeProjectFile1.FilePath));
DocumentItems.Property(Path.GetFileName(TestProjectData.SomeProjectFile1.FilePath), Rules.RazorGenerateWithTargetPath.TargetPathProperty, TestProjectData.SomeProjectFile1.TargetPath);
var changes = new TestProjectChangeDescription[]
{
@ -882,7 +880,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
DocumentItems.ToChange(),
};
var services = new TestProjectSystemServices("c:\\MyProject\\Test.csproj");
var services = new TestProjectSystemServices(TestProjectData.SomeProject.FilePath);
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
@ -894,7 +892,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// Assert - 1
var snapshot = Assert.Single(ProjectManager.Projects);
Assert.Equal("c:\\MyProject\\Test.csproj", snapshot.FilePath);
Assert.Equal(TestProjectData.SomeProject.FilePath, snapshot.FilePath);
Assert.Equal(RazorLanguageVersion.Version_2_1, snapshot.Configuration.LanguageVersion);
Assert.Equal("MVC-2.1", snapshot.Configuration.ConfigurationName);
@ -937,8 +935,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
ExtensionItems.Item("MVC-2.1");
ExtensionItems.Item("Another-Thing");
DocumentItems.Item("File.cshtml");
DocumentItems.Property("File.cshtml", Rules.RazorGenerateWithTargetPath.TargetPathProperty, "File.cshtml");
DocumentItems.Item(Path.GetFileName(TestProjectData.SomeProjectFile1.FilePath));
DocumentItems.Property(Path.GetFileName(TestProjectData.SomeProjectFile1.FilePath), Rules.RazorGenerateWithTargetPath.TargetPathProperty, TestProjectData.SomeProjectFile1.TargetPath);
var changes = new TestProjectChangeDescription[]
{
@ -948,7 +946,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
DocumentItems.ToChange(),
};
var services = new TestProjectSystemServices("c:\\MyProject\\Test.csproj");
var services = new TestProjectSystemServices(TestProjectData.SomeProject.FilePath);
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
@ -960,7 +958,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// Assert - 1
var snapshot = Assert.Single(ProjectManager.Projects);
Assert.Equal("c:\\MyProject\\Test.csproj", snapshot.FilePath);
Assert.Equal(TestProjectData.SomeProject.FilePath, snapshot.FilePath);
Assert.Equal(RazorLanguageVersion.Version_2_1, snapshot.Configuration.LanguageVersion);
Assert.Equal("MVC-2.1", snapshot.Configuration.ConfigurationName);
@ -1007,8 +1005,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
ExtensionItems.Item("MVC-2.1");
ExtensionItems.Item("Another-Thing");
DocumentItems.Item("File.cshtml");
DocumentItems.Property("File.cshtml", Rules.RazorGenerateWithTargetPath.TargetPathProperty, "File.cshtml");
DocumentItems.Item(Path.GetFileName(TestProjectData.SomeProjectFile1.FilePath));
DocumentItems.Property(Path.GetFileName(TestProjectData.SomeProjectFile1.FilePath), Rules.RazorGenerateWithTargetPath.TargetPathProperty, TestProjectData.SomeProjectFile1.TargetPath);
var changes = new TestProjectChangeDescription[]
{
@ -1018,7 +1016,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
DocumentItems.ToChange(),
};
var services = new TestProjectSystemServices("c:\\MyProject\\Test.csproj");
var services = new TestProjectSystemServices(TestProjectData.SomeProject.FilePath);
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
@ -1030,16 +1028,16 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// Assert - 1
var snapshot = Assert.Single(ProjectManager.Projects);
Assert.Equal("c:\\MyProject\\Test.csproj", snapshot.FilePath);
Assert.Equal(TestProjectData.SomeProject.FilePath, snapshot.FilePath);
Assert.Same("MVC-2.1", snapshot.Configuration.ConfigurationName);
// Act - 2
services.UnconfiguredProject.FullPath = "c:\\AnotherProject\\Test2.csproj";
services.UnconfiguredProject.FullPath = TestProjectData.AnotherProject.FilePath;
await Task.Run(async () => await host.OnProjectRenamingAsync());
// Assert - 1
snapshot = Assert.Single(ProjectManager.Projects);
Assert.Equal("c:\\AnotherProject\\Test2.csproj", snapshot.FilePath);
Assert.Equal(TestProjectData.AnotherProject.FilePath, snapshot.FilePath);
Assert.Same("MVC-2.1", snapshot.Configuration.ConfigurationName);
await Task.Run(async () => await host.DisposeAsync());

Просмотреть файл

@ -12,11 +12,10 @@ using ItemReference = Microsoft.CodeAnalysis.Razor.ProjectSystem.ManagedProjectS
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
public class FallbackRazorProjectHostTest : ForegroundDispatcherTestBase
public class FallbackRazorProjectHostTest : ForegroundDispatcherWorkspaceTestBase
{
public FallbackRazorProjectHostTest()
{
Workspace = new AdhocWorkspace();
ProjectManager = new TestProjectSnapshotManager(Dispatcher, Workspace);
ReferenceItems = new ItemCollection(ManagedProjectSystemSchema.ResolvedCompilationReference.SchemaName);
@ -32,8 +31,6 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
private ItemCollection NoneItems { get; }
private Workspace Workspace { get; }
[Fact]
public void GetChangedAndRemovedDocuments_ReturnsChangedContentAndNoneItems()
{

Просмотреть файл

@ -9,11 +9,10 @@ using Xunit;
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
public class WorkspaceProjectSnapshotChangeTriggerTest : ForegroundDispatcherTestBase
public class WorkspaceProjectSnapshotChangeTriggerTest : ForegroundDispatcherWorkspaceTestBase
{
public WorkspaceProjectSnapshotChangeTriggerTest()
{
Workspace = TestWorkspace.Create();
EmptySolution = Workspace.CurrentSolution.GetIsolatedSolution();
var projectId1 = ProjectId.CreateNewId("One");
@ -72,8 +71,6 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
private Project ProjectNumberThree { get; }
private Workspace Workspace { get; }
[ForegroundTheory]
[InlineData(WorkspaceChangeKind.SolutionAdded)]
[InlineData(WorkspaceChangeKind.SolutionChanged)]

Просмотреть файл

@ -2,8 +2,8 @@
// 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;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.VisualStudio.Editor.Razor;
using Microsoft.VisualStudio.Shell.Interop;
@ -16,8 +16,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
{
public VsSolutionUpdatesProjectSnapshotChangeTriggerTest()
{
SomeProject = new HostProject("c:\\SomeProject\\SomeProject.csproj", FallbackRazorConfiguration.MVC_1_0);
SomeOtherProject = new HostProject("c:\\SomeOtherProject\\SomeOtherProject.csproj", FallbackRazorConfiguration.MVC_2_0);
SomeProject = new HostProject(TestProjectData.SomeProject.FilePath, FallbackRazorConfiguration.MVC_1_0);
SomeOtherProject = new HostProject(TestProjectData.AnotherProject.FilePath, FallbackRazorConfiguration.MVC_2_0);
Workspace = TestWorkspace.Create(w =>
{

Просмотреть файл

@ -8,6 +8,9 @@
<Compile Include="..\Microsoft.CodeAnalysis.Razor.Workspaces.Test\Shared\**\*.cs">
<Link>Shared\%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="..\Microsoft.VisualStudio.Editor.Razor.Test\Shared\**\*.cs">
<Link>Shared\%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
</ItemGroup>
<ItemGroup>