Added a taghelpers and imports overload to Process and ProcessDesignTime
We want to have a way to specify the taghelper descriptors and imports to use while processing a specific document. - Added an overload to Process and ProcessDesignTime to take in a list TagHelperDescriptors and a list of imports - Added the corresponding CreateCodeDocumentCore overload - Added GetTagHelpers and SetTagHelpers extension methods for CodeDocument - Added the necessary plumbing to use the taghelpers from the CodeDocument when available and fallback logic. - Added DocumentImportsTracker and updated background code generation logic to use the new overload - Added/updated tests
This commit is contained in:
parent
a20a88fa3f
commit
f9d4fba39d
|
|
@ -68,10 +68,23 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
var importItems = importFeature.GetImports(projectItem);
|
||||
var importSourceDocuments = GetImportSourceDocuments(importItems);
|
||||
|
||||
return CreateCodeDocumentCore(sourceDocument, importSourceDocuments, tagHelpers: null);
|
||||
}
|
||||
|
||||
internal override RazorCodeDocument CreateCodeDocumentCore(RazorSourceDocument sourceDocument, IReadOnlyList<RazorSourceDocument> importSourceDocuments, IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||
{
|
||||
if (sourceDocument == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(sourceDocument));
|
||||
}
|
||||
|
||||
var parserOptions = GetRequiredFeature<IRazorParserOptionsFactoryProjectFeature>().Create(ConfigureParserOptions);
|
||||
var codeGenerationOptions = GetRequiredFeature<IRazorCodeGenerationOptionsFactoryProjectFeature>().Create(ConfigureCodeGenerationOptions);
|
||||
|
||||
return RazorCodeDocument.Create(sourceDocument, importSourceDocuments, parserOptions, codeGenerationOptions);
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument, importSourceDocuments, parserOptions, codeGenerationOptions);
|
||||
codeDocument.SetTagHelpers(tagHelpers);
|
||||
|
||||
return codeDocument;
|
||||
}
|
||||
|
||||
protected override RazorCodeDocument CreateCodeDocumentDesignTimeCore(RazorProjectItem projectItem)
|
||||
|
|
@ -87,10 +100,23 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
var importItems = importFeature.GetImports(projectItem);
|
||||
var importSourceDocuments = GetImportSourceDocuments(importItems, suppressExceptions: true);
|
||||
|
||||
return CreateCodeDocumentDesignTimeCore(sourceDocument, importSourceDocuments, tagHelpers: null);
|
||||
}
|
||||
|
||||
internal override RazorCodeDocument CreateCodeDocumentDesignTimeCore(RazorSourceDocument sourceDocument, IReadOnlyList<RazorSourceDocument> importSourceDocuments, IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||
{
|
||||
if (sourceDocument == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(sourceDocument));
|
||||
}
|
||||
|
||||
var parserOptions = GetRequiredFeature<IRazorParserOptionsFactoryProjectFeature>().Create(ConfigureDesignTimeParserOptions);
|
||||
var codeGenerationOptions = GetRequiredFeature<IRazorCodeGenerationOptionsFactoryProjectFeature>().Create(ConfigureDesignTimeCodeGenerationOptions);
|
||||
|
||||
return RazorCodeDocument.Create(sourceDocument, importSourceDocuments, parserOptions, codeGenerationOptions);
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument, importSourceDocuments, parserOptions, codeGenerationOptions);
|
||||
codeDocument.SetTagHelpers(tagHelpers);
|
||||
|
||||
return codeDocument;
|
||||
}
|
||||
|
||||
protected override void ProcessCore(RazorCodeDocument codeDocument)
|
||||
|
|
|
|||
|
|
@ -15,18 +15,23 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
var syntaxTree = codeDocument.GetSyntaxTree();
|
||||
ThrowForMissingDocumentDependency(syntaxTree);
|
||||
|
||||
var feature = Engine.Features.OfType<ITagHelperFeature>().FirstOrDefault();
|
||||
if (feature == null)
|
||||
var descriptors = codeDocument.GetTagHelpers();
|
||||
if (descriptors == null)
|
||||
{
|
||||
// No feature, nothing to do.
|
||||
return;
|
||||
var feature = Engine.Features.OfType<ITagHelperFeature>().FirstOrDefault();
|
||||
if (feature == null)
|
||||
{
|
||||
// No feature, nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
descriptors = feature.GetDescriptors();
|
||||
}
|
||||
|
||||
// We need to find directives in all of the *imports* as well as in the main razor file
|
||||
//
|
||||
// The imports come logically before the main razor file and are in the order they
|
||||
// should be processed.
|
||||
var descriptors = feature.GetDescriptors();
|
||||
var visitor = new DirectiveVisitor(descriptors);
|
||||
var imports = codeDocument.GetImportSyntaxTrees();
|
||||
if (imports != null)
|
||||
|
|
|
|||
|
|
@ -29,6 +29,26 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
document.Items[typeof(TagHelperDocumentContext)] = context;
|
||||
}
|
||||
|
||||
internal static IReadOnlyList<TagHelperDescriptor> GetTagHelpers(this RazorCodeDocument document)
|
||||
{
|
||||
if (document == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(document));
|
||||
}
|
||||
|
||||
return (document.Items[typeof(TagHelpersHolder)] as TagHelpersHolder)?.TagHelpers;
|
||||
}
|
||||
|
||||
internal static void SetTagHelpers(this RazorCodeDocument document, IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||
{
|
||||
if (document == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(document));
|
||||
}
|
||||
|
||||
document.Items[typeof(TagHelpersHolder)] = new TagHelpersHolder(tagHelpers);
|
||||
}
|
||||
|
||||
public static RazorSyntaxTree GetSyntaxTree(this RazorCodeDocument document)
|
||||
{
|
||||
if (document == null)
|
||||
|
|
@ -168,5 +188,15 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
|
||||
public IReadOnlyList<RazorSyntaxTree> SyntaxTrees { get; }
|
||||
}
|
||||
|
||||
private class TagHelpersHolder
|
||||
{
|
||||
public TagHelpersHolder(IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||
{
|
||||
TagHelpers = tagHelpers;
|
||||
}
|
||||
|
||||
public IReadOnlyList<TagHelperDescriptor> TagHelpers { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,18 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
return codeDocument;
|
||||
}
|
||||
|
||||
internal virtual RazorCodeDocument Process(RazorSourceDocument source, IReadOnlyList<RazorSourceDocument> importSources, IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
}
|
||||
|
||||
var codeDocument = CreateCodeDocumentCore(source, importSources, tagHelpers);
|
||||
ProcessCore(codeDocument);
|
||||
return codeDocument;
|
||||
}
|
||||
|
||||
public virtual RazorCodeDocument ProcessDesignTime(RazorProjectItem projectItem)
|
||||
{
|
||||
if (projectItem == null)
|
||||
|
|
@ -47,10 +59,32 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
return codeDocument;
|
||||
}
|
||||
|
||||
internal virtual RazorCodeDocument ProcessDesignTime(RazorSourceDocument source, IReadOnlyList<RazorSourceDocument> importSources, IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
}
|
||||
|
||||
var codeDocument = CreateCodeDocumentDesignTimeCore(source, importSources, tagHelpers);
|
||||
ProcessCore(codeDocument);
|
||||
return codeDocument;
|
||||
}
|
||||
|
||||
protected abstract RazorCodeDocument CreateCodeDocumentCore(RazorProjectItem projectItem);
|
||||
|
||||
internal virtual RazorCodeDocument CreateCodeDocumentCore(RazorSourceDocument source, IReadOnlyList<RazorSourceDocument> importSources, IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||
{
|
||||
return RazorCodeDocument.Create(source, importSources);
|
||||
}
|
||||
|
||||
protected abstract RazorCodeDocument CreateCodeDocumentDesignTimeCore(RazorProjectItem projectItem);
|
||||
|
||||
internal virtual RazorCodeDocument CreateCodeDocumentDesignTimeCore(RazorSourceDocument source, IReadOnlyList<RazorSourceDocument> importSources, IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||
{
|
||||
return RazorCodeDocument.Create(source, importSources);
|
||||
}
|
||||
|
||||
protected abstract void ProcessCore(RazorCodeDocument codeDocument);
|
||||
|
||||
internal static RazorProjectEngine CreateEmpty(Action<RazorProjectEngineBuilder> configure = null)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
|
@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
internal class DefaultDocumentSnapshot : DocumentSnapshot
|
||||
{
|
||||
public DefaultDocumentSnapshot(ProjectSnapshot project, DocumentState state)
|
||||
public DefaultDocumentSnapshot(DefaultProjectSnapshot project, DocumentState state)
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
|
|
@ -27,7 +27,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
State = state;
|
||||
}
|
||||
|
||||
public ProjectSnapshot Project { get; }
|
||||
public DefaultProjectSnapshot Project { get; }
|
||||
|
||||
public DocumentState State { get; }
|
||||
|
||||
|
|
@ -35,6 +35,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
public override string TargetPath => State.HostDocument.TargetPath;
|
||||
|
||||
public override IReadOnlyList<DocumentSnapshot> GetImports()
|
||||
{
|
||||
return State.Imports.GetImports(Project, this);
|
||||
}
|
||||
|
||||
public override Task<SourceText> GetTextAsync()
|
||||
{
|
||||
return State.GetTextAsync();
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
|
|
@ -16,6 +18,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
private Task<RazorCodeDocument> _task;
|
||||
|
||||
private IReadOnlyList<TagHelperDescriptor> _tagHelpers;
|
||||
private IReadOnlyList<ImportItem> _imports;
|
||||
|
||||
public DocumentGeneratedOutputTracker(DocumentGeneratedOutputTracker older)
|
||||
{
|
||||
|
|
@ -62,13 +65,19 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
private async Task<RazorCodeDocument> GetGeneratedOutputInitializationTaskCore(ProjectSnapshot project, DocumentSnapshot document)
|
||||
{
|
||||
var tagHelpers = await project.GetTagHelpersAsync().ConfigureAwait(false);
|
||||
var imports = await GetImportsAsync(project, document);
|
||||
|
||||
if (_older != null && _older.IsResultAvailable)
|
||||
{
|
||||
var difference = new HashSet<TagHelperDescriptor>(TagHelperDescriptorComparer.Default);
|
||||
difference.UnionWith(_older._tagHelpers);
|
||||
difference.SymmetricExceptWith(tagHelpers);
|
||||
var tagHelperDifference = new HashSet<TagHelperDescriptor>(TagHelperDescriptorComparer.Default);
|
||||
tagHelperDifference.UnionWith(_older._tagHelpers);
|
||||
tagHelperDifference.SymmetricExceptWith(tagHelpers);
|
||||
|
||||
if (difference.Count == 0)
|
||||
var importDifference = new HashSet<ImportItem>();
|
||||
importDifference.UnionWith(_older._imports);
|
||||
importDifference.SymmetricExceptWith(imports);
|
||||
|
||||
if (tagHelperDifference.Count == 0 && importDifference.Count == 0)
|
||||
{
|
||||
// We can use the cached result.
|
||||
var result = _older._task.Result;
|
||||
|
|
@ -76,8 +85,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// Drop reference so it can be GC'ed
|
||||
_older = null;
|
||||
|
||||
// Cache the tag helpers so the next version can use them
|
||||
// Cache the tag helpers and imports so the next version can use them
|
||||
_tagHelpers = tagHelpers;
|
||||
_imports = imports;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -86,13 +96,77 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// Drop reference so it can be GC'ed
|
||||
_older = null;
|
||||
|
||||
|
||||
// Cache the tag helpers so the next version can use them
|
||||
// Cache the tag helpers and imports so the next version can use them
|
||||
_tagHelpers = tagHelpers;
|
||||
_imports = imports;
|
||||
|
||||
var importSources = new List<RazorSourceDocument>();
|
||||
foreach (var item in imports)
|
||||
{
|
||||
var sourceDocument = await GetRazorSourceDocumentAsync(item.Import);
|
||||
importSources.Add(sourceDocument);
|
||||
}
|
||||
|
||||
var documentSource = await GetRazorSourceDocumentAsync(document);
|
||||
|
||||
var projectEngine = project.GetProjectEngine();
|
||||
var projectItem = projectEngine.FileSystem.GetItem(document.FilePath);
|
||||
return projectItem == null ? null : projectEngine.ProcessDesignTime(projectItem);
|
||||
|
||||
return projectEngine.ProcessDesignTime(documentSource, importSources, tagHelpers);
|
||||
}
|
||||
|
||||
private async Task<RazorSourceDocument> GetRazorSourceDocumentAsync(DocumentSnapshot document)
|
||||
{
|
||||
var sourceText = await document.GetTextAsync();
|
||||
|
||||
return sourceText.GetRazorSourceDocument(document.FilePath);
|
||||
}
|
||||
|
||||
private async Task<IReadOnlyList<ImportItem>> GetImportsAsync(ProjectSnapshot project, DocumentSnapshot document)
|
||||
{
|
||||
var imports = new List<ImportItem>();
|
||||
foreach (var snapshot in document.GetImports())
|
||||
{
|
||||
var versionStamp = await snapshot.GetTextVersionAsync();
|
||||
imports.Add(new ImportItem(snapshot.FilePath, versionStamp, snapshot));
|
||||
}
|
||||
|
||||
return imports;
|
||||
}
|
||||
|
||||
private struct ImportItem : IEquatable<ImportItem>
|
||||
{
|
||||
public ImportItem(string filePath, VersionStamp versionStamp, DocumentSnapshot import)
|
||||
{
|
||||
FilePath = filePath;
|
||||
VersionStamp = versionStamp;
|
||||
Import = import;
|
||||
}
|
||||
|
||||
public string FilePath { get; }
|
||||
|
||||
public VersionStamp VersionStamp { get; }
|
||||
|
||||
public DocumentSnapshot Import { get; }
|
||||
|
||||
public bool Equals(ImportItem other)
|
||||
{
|
||||
return
|
||||
FilePathComparer.Instance.Equals(FilePath, other.FilePath) &&
|
||||
VersionStamp == other.VersionStamp;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is ImportItem item ? Equals(item) : false;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hash = new HashCodeCombiner();
|
||||
hash.Add(FilePath, FilePathComparer.Instance);
|
||||
hash.Add(VersionStamp);
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,165 @@
|
|||
// 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.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal class DocumentImportsTracker
|
||||
{
|
||||
private readonly object _lock;
|
||||
|
||||
private IReadOnlyList<DocumentSnapshot> _imports;
|
||||
|
||||
public DocumentImportsTracker()
|
||||
{
|
||||
_lock = new object();
|
||||
}
|
||||
|
||||
public IReadOnlyList<DocumentSnapshot> GetImports(ProjectSnapshot project, DocumentSnapshot document)
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
if (document == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(document));
|
||||
}
|
||||
|
||||
if (_imports == null)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_imports == null)
|
||||
{
|
||||
_imports = GetImportsCore(project, document);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _imports;
|
||||
}
|
||||
|
||||
private IReadOnlyList<DocumentSnapshot> GetImportsCore(ProjectSnapshot project, DocumentSnapshot document)
|
||||
{
|
||||
var projectEngine = project.GetProjectEngine();
|
||||
var importFeature = projectEngine.ProjectFeatures.OfType<IImportProjectFeature>().FirstOrDefault();
|
||||
var projectItem = projectEngine.FileSystem.GetItem(document.FilePath);
|
||||
var importItems = importFeature?.GetImports(projectItem).Where(i => i.Exists);
|
||||
if (importItems == null)
|
||||
{
|
||||
return Array.Empty<DocumentSnapshot>();
|
||||
}
|
||||
|
||||
var imports = new List<DocumentSnapshot>();
|
||||
foreach (var item in importItems)
|
||||
{
|
||||
if (item.PhysicalPath == null)
|
||||
{
|
||||
// This is a default import.
|
||||
var defaultImport = new DefaultImportDocumentSnapshot(project, item);
|
||||
imports.Add(defaultImport);
|
||||
}
|
||||
else
|
||||
{
|
||||
var import = project.GetDocument(item.PhysicalPath);
|
||||
if (import == null)
|
||||
{
|
||||
// We are not tracking this document in this project. So do nothing.
|
||||
continue;
|
||||
}
|
||||
|
||||
imports.Add(import);
|
||||
}
|
||||
}
|
||||
|
||||
return imports;
|
||||
}
|
||||
|
||||
private class DefaultImportDocumentSnapshot : DocumentSnapshot
|
||||
{
|
||||
private ProjectSnapshot _project;
|
||||
private RazorProjectItem _importItem;
|
||||
private SourceText _sourceText;
|
||||
private VersionStamp _version;
|
||||
private DocumentGeneratedOutputTracker _generatedOutput;
|
||||
|
||||
public DefaultImportDocumentSnapshot(ProjectSnapshot project, RazorProjectItem item)
|
||||
{
|
||||
_project = project;
|
||||
_importItem = item;
|
||||
_version = VersionStamp.Default;
|
||||
_generatedOutput = new DocumentGeneratedOutputTracker(null);
|
||||
}
|
||||
|
||||
public override string FilePath => null;
|
||||
|
||||
public override string TargetPath => null;
|
||||
|
||||
public override Task<RazorCodeDocument> GetGeneratedOutputAsync()
|
||||
{
|
||||
return _generatedOutput.GetGeneratedOutputInitializationTask(_project, this);
|
||||
}
|
||||
|
||||
public override IReadOnlyList<DocumentSnapshot> GetImports()
|
||||
{
|
||||
return Array.Empty<DocumentSnapshot>();
|
||||
}
|
||||
|
||||
public async override Task<SourceText> GetTextAsync()
|
||||
{
|
||||
using (var stream = _importItem.Read())
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
var content = await reader.ReadToEndAsync();
|
||||
_sourceText = SourceText.From(content);
|
||||
}
|
||||
|
||||
return _sourceText;
|
||||
}
|
||||
|
||||
public override Task<VersionStamp> GetTextVersionAsync()
|
||||
{
|
||||
return Task.FromResult(_version);
|
||||
}
|
||||
|
||||
public override bool TryGetText(out SourceText result)
|
||||
{
|
||||
if (_sourceText != null)
|
||||
{
|
||||
result = _sourceText;
|
||||
return true;
|
||||
}
|
||||
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool TryGetTextVersion(out VersionStamp result)
|
||||
{
|
||||
result = _version;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool TryGetGeneratedOutput(out RazorCodeDocument result)
|
||||
{
|
||||
if (_generatedOutput.IsResultAvailable)
|
||||
{
|
||||
result = GetGeneratedOutputAsync().Result;
|
||||
return true;
|
||||
}
|
||||
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
|
@ -14,6 +14,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
public abstract string TargetPath { get; }
|
||||
|
||||
public abstract IReadOnlyList<DocumentSnapshot> GetImports();
|
||||
|
||||
public abstract Task<SourceText> GetTextAsync();
|
||||
|
||||
public abstract Task<VersionStamp> GetTextVersionAsync();
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
private VersionStamp? _version;
|
||||
|
||||
private DocumentGeneratedOutputTracker _generatedOutput;
|
||||
private DocumentImportsTracker _imports;
|
||||
|
||||
public static DocumentState Create(
|
||||
HostWorkspaceServices services,
|
||||
|
|
@ -44,7 +45,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
return new DocumentState(services, hostDocument, null, null, loader);
|
||||
}
|
||||
|
||||
private DocumentState(
|
||||
// Internal for testing
|
||||
internal DocumentState(
|
||||
HostWorkspaceServices services,
|
||||
HostDocument hostDocument,
|
||||
SourceText text,
|
||||
|
|
@ -82,6 +84,25 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
}
|
||||
}
|
||||
|
||||
public DocumentImportsTracker Imports
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_imports == null)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_imports == null)
|
||||
{
|
||||
_imports = new DocumentImportsTracker();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _imports;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<SourceText> GetTextAsync()
|
||||
{
|
||||
if (TryGetText(out var text))
|
||||
|
|
@ -148,7 +169,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
return false;
|
||||
}
|
||||
|
||||
public DocumentState WithConfigurationChange()
|
||||
public virtual DocumentState WithConfigurationChange()
|
||||
{
|
||||
var state = new DocumentState(Services, HostDocument, _sourceText, _version, _loader);
|
||||
|
||||
|
|
@ -160,7 +181,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
return state;
|
||||
}
|
||||
|
||||
public DocumentState WithWorkspaceProjectChange()
|
||||
public virtual DocumentState WithWorkspaceProjectChange()
|
||||
{
|
||||
var state = new DocumentState(Services, HostDocument, _sourceText, _version, _loader);
|
||||
|
||||
|
|
@ -175,7 +196,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
return state;
|
||||
}
|
||||
|
||||
public DocumentState WithText(SourceText sourceText, VersionStamp version)
|
||||
public virtual DocumentState WithText(SourceText sourceText, VersionStamp version)
|
||||
{
|
||||
if (sourceText == null)
|
||||
{
|
||||
|
|
@ -185,8 +206,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
return new DocumentState(Services, HostDocument, sourceText, version, null);
|
||||
}
|
||||
|
||||
|
||||
public DocumentState WithTextLoader(Func<Task<TextAndVersion>> loader)
|
||||
public virtual DocumentState WithTextLoader(Func<Task<TextAndVersion>> loader)
|
||||
{
|
||||
if (loader == null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -83,7 +83,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
_tagHelpers = older._tagHelpers?.ForkFor(this, difference);
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<string, DocumentState> Documents { get; }
|
||||
// Internal set for testing.
|
||||
public IReadOnlyDictionary<string, DocumentState> Documents { get; internal set; }
|
||||
|
||||
public HostProject HostProject { get; }
|
||||
|
||||
|
|
@ -293,7 +294,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
var documents = new Dictionary<string, DocumentState>(FilePathComparer.Instance);
|
||||
foreach (var kvp in Documents)
|
||||
{
|
||||
documents.Add(kvp.Key, kvp.Value.WithConfigurationChange());
|
||||
documents.Add(kvp.Key, kvp.Value.WithWorkspaceProjectChange());
|
||||
}
|
||||
|
||||
var state = new ProjectState(this, difference, HostProject, workspaceProject, documents);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Text
|
||||
{
|
||||
internal static class SourceTextExtensions
|
||||
{
|
||||
public static RazorSourceDocument GetRazorSourceDocument(this SourceText sourceText, string fileName)
|
||||
{
|
||||
if (sourceText == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(sourceText));
|
||||
}
|
||||
|
||||
var content = sourceText.ToString();
|
||||
|
||||
return RazorSourceDocument.Create(content, fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -32,6 +32,16 @@ namespace Microsoft.VisualStudio.Editor.Razor.Documents
|
|||
_onClosed = Document_Closed;
|
||||
}
|
||||
|
||||
// For testing purposes only.
|
||||
internal EditorDocumentManagerListener(EditorDocumentManager documentManager, EventHandler onChangedOnDisk, EventHandler onChangedInEditor, EventHandler onOpened, EventHandler onClosed)
|
||||
{
|
||||
_documentManager = documentManager;
|
||||
_onChangedOnDisk = onChangedOnDisk;
|
||||
_onChangedInEditor = onChangedInEditor;
|
||||
_onOpened = onOpened;
|
||||
_onClosed = onClosed;
|
||||
}
|
||||
|
||||
public override void Initialize(ProjectSnapshotManagerBase projectManager)
|
||||
{
|
||||
if (projectManager == null)
|
||||
|
|
@ -45,17 +55,18 @@ namespace Microsoft.VisualStudio.Editor.Razor.Documents
|
|||
_projectManager.Changed += ProjectManager_Changed;
|
||||
}
|
||||
|
||||
private void ProjectManager_Changed(object sender, ProjectChangeEventArgs e)
|
||||
// Internal for testing.
|
||||
internal void ProjectManager_Changed(object sender, ProjectChangeEventArgs e)
|
||||
{
|
||||
switch (e.Kind)
|
||||
{
|
||||
case ProjectChangeKind.DocumentAdded:
|
||||
{
|
||||
var key = new DocumentKey(e.ProjectFilePath, e.DocumentFilePath);
|
||||
var document = _documentManager.GetOrCreateDocument(key, _onChangedOnDisk, _onChangedOnDisk, _onOpened, _onClosed);
|
||||
var document = _documentManager.GetOrCreateDocument(key, _onChangedOnDisk, _onChangedInEditor, _onOpened, _onClosed);
|
||||
if (document.IsOpenInEditor)
|
||||
{
|
||||
Document_Opened(document, EventArgs.Empty);
|
||||
_onOpened(document, EventArgs.Empty);
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
|
@ -91,5 +93,147 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
Assert.NotNull(csharpDocument);
|
||||
Assert.Empty(csharpDocument.Diagnostics);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Process_WithImportsAndTagHelpers_SetsOnCodeDocument()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new TestRazorProjectItem("Index.cshtml");
|
||||
var importItem = new TestRazorProjectItem("_import.cshtml");
|
||||
var expectedImports = new[] { RazorSourceDocument.ReadFrom(importItem) };
|
||||
var expectedTagHelpers = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("TestTagHelper", "TestAssembly").Build(),
|
||||
TagHelperDescriptorBuilder.Create("Test2TagHelper", "TestAssembly").Build(),
|
||||
};
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, TestRazorProjectFileSystem.Empty);
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.Process(RazorSourceDocument.ReadFrom(projectItem), expectedImports, expectedTagHelpers);
|
||||
|
||||
// Assert
|
||||
var tagHelpers = codeDocument.GetTagHelpers();
|
||||
Assert.Same(expectedTagHelpers, tagHelpers);
|
||||
Assert.Equal(expectedImports, codeDocument.Imports);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Process_WithNullTagHelpers_SetsOnCodeDocument()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new TestRazorProjectItem("Index.cshtml");
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, TestRazorProjectFileSystem.Empty);
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.Process(RazorSourceDocument.ReadFrom(projectItem), Array.Empty<RazorSourceDocument>(), tagHelpers: null);
|
||||
|
||||
// Assert
|
||||
var tagHelpers = codeDocument.GetTagHelpers();
|
||||
Assert.Null(tagHelpers);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Process_SetsNullTagHelpersOnCodeDocument()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new TestRazorProjectItem("Index.cshtml");
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, TestRazorProjectFileSystem.Empty);
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.Process(projectItem);
|
||||
|
||||
// Assert
|
||||
var tagHelpers = codeDocument.GetTagHelpers();
|
||||
Assert.Null(tagHelpers);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Process_WithNullImports_SetsEmptyListOnCodeDocument()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new TestRazorProjectItem("Index.cshtml");
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, TestRazorProjectFileSystem.Empty);
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.Process(RazorSourceDocument.ReadFrom(projectItem), importSources: null, tagHelpers: null);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(codeDocument.Imports);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcessDesignTime_WithImportsAndTagHelpers_SetsOnCodeDocument()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new TestRazorProjectItem("Index.cshtml");
|
||||
var importItem = new TestRazorProjectItem("_import.cshtml");
|
||||
var expectedImports = new[] { RazorSourceDocument.ReadFrom(importItem) };
|
||||
var expectedTagHelpers = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("TestTagHelper", "TestAssembly").Build(),
|
||||
TagHelperDescriptorBuilder.Create("Test2TagHelper", "TestAssembly").Build(),
|
||||
};
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, TestRazorProjectFileSystem.Empty);
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.ProcessDesignTime(RazorSourceDocument.ReadFrom(projectItem), expectedImports, expectedTagHelpers);
|
||||
|
||||
// Assert
|
||||
var tagHelpers = codeDocument.GetTagHelpers();
|
||||
Assert.Same(expectedTagHelpers, tagHelpers);
|
||||
Assert.Equal(expectedImports, codeDocument.Imports);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcessDesignTime_WithNullTagHelpers_SetsOnCodeDocument()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new TestRazorProjectItem("Index.cshtml");
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, TestRazorProjectFileSystem.Empty);
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.ProcessDesignTime(RazorSourceDocument.ReadFrom(projectItem), Array.Empty<RazorSourceDocument>(), tagHelpers: null);
|
||||
|
||||
// Assert
|
||||
var tagHelpers = codeDocument.GetTagHelpers();
|
||||
Assert.Null(tagHelpers);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcessDesignTime_SetsNullTagHelpersOnCodeDocument()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new TestRazorProjectItem("Index.cshtml");
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, TestRazorProjectFileSystem.Empty);
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.ProcessDesignTime(projectItem);
|
||||
|
||||
// Assert
|
||||
var tagHelpers = codeDocument.GetTagHelpers();
|
||||
Assert.Null(tagHelpers);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcessDesignTime_WithNullImports_SetsEmptyListOnCodeDocument()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new TestRazorProjectItem("Index.cshtml");
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, TestRazorProjectFileSystem.Empty);
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.ProcessDesignTime(RazorSourceDocument.ReadFrom(projectItem), importSources: null, tagHelpers: null);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(codeDocument.Imports);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -171,6 +171,129 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
Assert.Equal("input", inputTagHelper.TagName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_WithTagHelperDescriptorsFromCodeDocument_RewritesTagHelpers()
|
||||
{
|
||||
// Arrange
|
||||
var projectEngine = RazorProjectEngine.Create();
|
||||
var tagHelpers = new[]
|
||||
{
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "form",
|
||||
typeName: "TestFormTagHelper",
|
||||
assemblyName: "TestAssembly"),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "TestInputTagHelper",
|
||||
assemblyName: "TestAssembly"),
|
||||
};
|
||||
|
||||
var phase = new DefaultRazorTagHelperBinderPhase()
|
||||
{
|
||||
Engine = projectEngine.Engine,
|
||||
};
|
||||
|
||||
var sourceDocument = CreateTestSourceDocument();
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
var originalTree = RazorSyntaxTree.Parse(sourceDocument);
|
||||
codeDocument.SetSyntaxTree(originalTree);
|
||||
codeDocument.SetTagHelpers(tagHelpers);
|
||||
|
||||
// Act
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
// Assert
|
||||
var rewrittenTree = codeDocument.GetSyntaxTree();
|
||||
Assert.Empty(rewrittenTree.Diagnostics);
|
||||
Assert.Equal(3, rewrittenTree.Root.Children.Count);
|
||||
var formTagHelper = Assert.IsType<TagHelperBlock>(rewrittenTree.Root.Children[2]);
|
||||
Assert.Equal("form", formTagHelper.TagName);
|
||||
Assert.Equal(3, formTagHelper.Children.Count);
|
||||
var inputTagHelper = Assert.IsType<TagHelperBlock>(formTagHelper.Children[1]);
|
||||
Assert.Equal("input", inputTagHelper.TagName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_NullTagHelperDescriptorsFromCodeDocument_FallsBackToTagHelperFeature()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelpers = new[]
|
||||
{
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "form",
|
||||
typeName: "TestFormTagHelper",
|
||||
assemblyName: "TestAssembly"),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "TestInputTagHelper",
|
||||
assemblyName: "TestAssembly"),
|
||||
};
|
||||
var projectEngine = RazorProjectEngine.Create(builder => builder.AddTagHelpers(tagHelpers));
|
||||
|
||||
var phase = new DefaultRazorTagHelperBinderPhase()
|
||||
{
|
||||
Engine = projectEngine.Engine,
|
||||
};
|
||||
|
||||
var sourceDocument = CreateTestSourceDocument();
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
var originalTree = RazorSyntaxTree.Parse(sourceDocument);
|
||||
codeDocument.SetSyntaxTree(originalTree);
|
||||
codeDocument.SetTagHelpers(tagHelpers: null);
|
||||
|
||||
// Act
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
// Assert
|
||||
var rewrittenTree = codeDocument.GetSyntaxTree();
|
||||
Assert.Empty(rewrittenTree.Diagnostics);
|
||||
Assert.Equal(3, rewrittenTree.Root.Children.Count);
|
||||
var formTagHelper = Assert.IsType<TagHelperBlock>(rewrittenTree.Root.Children[2]);
|
||||
Assert.Equal("form", formTagHelper.TagName);
|
||||
Assert.Equal(3, formTagHelper.Children.Count);
|
||||
var inputTagHelper = Assert.IsType<TagHelperBlock>(formTagHelper.Children[1]);
|
||||
Assert.Equal("input", inputTagHelper.TagName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_EmptyTagHelperDescriptorsFromCodeDocument_DoesNotFallbackToTagHelperFeature()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelpers = new[]
|
||||
{
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "form",
|
||||
typeName: "TestFormTagHelper",
|
||||
assemblyName: "TestAssembly"),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "TestInputTagHelper",
|
||||
assemblyName: "TestAssembly"),
|
||||
};
|
||||
var projectEngine = RazorProjectEngine.Create(builder => builder.AddTagHelpers(tagHelpers));
|
||||
|
||||
var phase = new DefaultRazorTagHelperBinderPhase()
|
||||
{
|
||||
Engine = projectEngine.Engine,
|
||||
};
|
||||
|
||||
var sourceDocument = CreateTestSourceDocument();
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
var originalTree = RazorSyntaxTree.Parse(sourceDocument);
|
||||
codeDocument.SetSyntaxTree(originalTree);
|
||||
codeDocument.SetTagHelpers(tagHelpers: Array.Empty<TagHelperDescriptor>());
|
||||
|
||||
// Act
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
// Assert
|
||||
var rewrittenTree = codeDocument.GetSyntaxTree();
|
||||
Assert.Empty(rewrittenTree.Diagnostics);
|
||||
Assert.Equal(7, rewrittenTree.Root.Children.Count);
|
||||
var rewrittenNodes = rewrittenTree.Root.Children.OfType<TagHelperBlock>();
|
||||
Assert.Empty(rewrittenNodes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_DirectiveWithoutQuotes_RewritesTagHelpers_TagHelperMatchesElementTwice()
|
||||
{
|
||||
|
|
@ -278,35 +401,58 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_NoopsWhenNoTagHelperFeature()
|
||||
public void Execute_TagHelpersFromCodeDocumentAndFeature_PrefersCodeDocument()
|
||||
{
|
||||
// Arrange
|
||||
var projectEngine = RazorProjectEngine.Create();
|
||||
var featureTagHelpers = new[]
|
||||
{
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "TestInputTagHelper",
|
||||
assemblyName: "TestAssembly"),
|
||||
};
|
||||
var projectEngine = RazorProjectEngine.Create(builder => builder.AddTagHelpers(featureTagHelpers));
|
||||
|
||||
var phase = new DefaultRazorTagHelperBinderPhase()
|
||||
{
|
||||
Engine = projectEngine.Engine,
|
||||
};
|
||||
|
||||
var sourceDocument = CreateTestSourceDocument();
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
var originalTree = RazorSyntaxTree.Parse(sourceDocument);
|
||||
codeDocument.SetSyntaxTree(originalTree);
|
||||
|
||||
var codeDocumentTagHelpers = new[]
|
||||
{
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "form",
|
||||
typeName: "TestFormTagHelper",
|
||||
assemblyName: "TestAssembly"),
|
||||
};
|
||||
codeDocument.SetTagHelpers(codeDocumentTagHelpers);
|
||||
|
||||
// Act
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
// Assert
|
||||
var outputTree = codeDocument.GetSyntaxTree();
|
||||
Assert.Empty(outputTree.Diagnostics);
|
||||
Assert.Same(originalTree, outputTree);
|
||||
var rewrittenTree = codeDocument.GetSyntaxTree();
|
||||
Assert.Empty(rewrittenTree.Diagnostics);
|
||||
Assert.Equal(3, rewrittenTree.Root.Children.Count);
|
||||
var formTagHelper = Assert.IsType<TagHelperBlock>(rewrittenTree.Root.Children[2]);
|
||||
Assert.Equal("form", formTagHelper.TagName);
|
||||
Assert.Collection(
|
||||
formTagHelper.Children,
|
||||
node => Assert.IsNotType<TagHelperBlock>(node),
|
||||
node => Assert.IsNotType<TagHelperBlock>(node),
|
||||
node => Assert.IsNotType<TagHelperBlock>(node));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_NoopsWhenNoFeature()
|
||||
public void Execute_NoopsWhenNoTagHelpersFromCodeDocumentOrFeature()
|
||||
{
|
||||
// Arrange
|
||||
var projectEngine = RazorProjectEngine.Create(builder =>
|
||||
{
|
||||
});
|
||||
var projectEngine = RazorProjectEngine.Create();
|
||||
var phase = new DefaultRazorTagHelperBinderPhase()
|
||||
{
|
||||
Engine = projectEngine.Engine,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language.Extensions;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -937,7 +938,6 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
|
|||
var projectEngine = CreateProjectEngine(builder =>
|
||||
{
|
||||
builder.ConfigureDocumentClassifier();
|
||||
builder.AddTagHelpers(descriptors);
|
||||
|
||||
// Some of these tests use templates
|
||||
builder.AddTargetExtension(new TemplateTargetExtension());
|
||||
|
|
@ -948,9 +948,10 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
|
|||
});
|
||||
|
||||
var projectItem = CreateProjectItem();
|
||||
var imports = GetImports(projectEngine, projectItem);
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.Process(projectItem);
|
||||
var codeDocument = projectEngine.Process(RazorSourceDocument.ReadFrom(projectItem), imports, descriptors.ToList());
|
||||
|
||||
// Assert
|
||||
AssertDocumentNodeMatchesBaseline(codeDocument.GetDocumentIntermediateNode());
|
||||
|
|
@ -963,7 +964,6 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
|
|||
var projectEngine = CreateProjectEngine(builder =>
|
||||
{
|
||||
builder.ConfigureDocumentClassifier();
|
||||
builder.AddTagHelpers(descriptors);
|
||||
|
||||
// Some of these tests use templates
|
||||
builder.AddTargetExtension(new TemplateTargetExtension());
|
||||
|
|
@ -974,14 +974,24 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
|
|||
});
|
||||
|
||||
var projectItem = CreateProjectItem();
|
||||
var imports = GetImports(projectEngine, projectItem);
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.ProcessDesignTime(projectItem);
|
||||
var codeDocument = projectEngine.ProcessDesignTime(RazorSourceDocument.ReadFrom(projectItem), imports, descriptors.ToList());
|
||||
|
||||
// Assert
|
||||
AssertDocumentNodeMatchesBaseline(codeDocument.GetDocumentIntermediateNode());
|
||||
AssertCSharpDocumentMatchesBaseline(codeDocument.GetCSharpDocument());
|
||||
AssertSourceMappingsMatchBaseline(codeDocument);
|
||||
}
|
||||
|
||||
private static IReadOnlyList<RazorSourceDocument> GetImports(RazorProjectEngine projectEngine, RazorProjectItem projectItem)
|
||||
{
|
||||
var importFeature = projectEngine.ProjectFeatures.OfType<IImportProjectFeature>().FirstOrDefault();
|
||||
var importItems = importFeature.GetImports(projectItem);
|
||||
var importSourceDocuments = importItems.Where(i => i.Exists).Select(i => RazorSourceDocument.ReadFrom(i)).ToList();
|
||||
|
||||
return importSourceDocuments;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,22 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
Assert.Same(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAndSetTagHelpers_ReturnsTagHelpers()
|
||||
{
|
||||
// Arrange
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
|
||||
var expected = new[] { TagHelperDescriptorBuilder.Create("TestTagHelper", "TestAssembly").Build() };
|
||||
codeDocument.SetTagHelpers(expected);
|
||||
|
||||
// Act
|
||||
var actual = codeDocument.GetTagHelpers();
|
||||
|
||||
// Assert
|
||||
Assert.Same(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetIRDocument_ReturnsIRDocument()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,12 +4,10 @@
|
|||
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
|
||||
|
|
@ -373,7 +371,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
Assert.NotSame(original.TagHelpers, state.TagHelpers);
|
||||
|
||||
Assert.NotSame(original.Documents[Documents[1].FilePath], state.Documents[Documents[1].FilePath]);
|
||||
Assert.NotSame(original.Documents[Documents[1].FilePath], state.Documents[Documents[1].FilePath]);
|
||||
Assert.NotSame(original.Documents[Documents[2].FilePath], state.Documents[Documents[2].FilePath]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -395,6 +393,30 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
Assert.Same(original, state);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectState_WithHostProject_CallsConfigurationChangeOnDocumentState()
|
||||
{
|
||||
// Arrange
|
||||
var callCount = 0;
|
||||
|
||||
var documents = new Dictionary<string, DocumentState>();
|
||||
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;
|
||||
|
||||
var changed = WorkspaceProject.WithAssemblyName("Test1");
|
||||
|
||||
// Act
|
||||
var state = original.WithHostProject(HostProjectWithConfigurationChange);
|
||||
|
||||
// Assert
|
||||
Assert.NotEqual(original.Version, state.Version);
|
||||
Assert.Same(HostProjectWithConfigurationChange, state.HostProject);
|
||||
Assert.Equal(2, callCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectState_WithWorkspaceProject_Removed()
|
||||
{
|
||||
|
|
@ -418,7 +440,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
Assert.NotSame(original.TagHelpers, state.TagHelpers);
|
||||
|
||||
Assert.NotSame(original.Documents[Documents[1].FilePath], state.Documents[Documents[1].FilePath]);
|
||||
Assert.NotSame(original.Documents[Documents[1].FilePath], state.Documents[Documents[1].FilePath]);
|
||||
Assert.NotSame(original.Documents[Documents[2].FilePath], state.Documents[Documents[2].FilePath]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -472,7 +494,92 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
Assert.NotSame(original.TagHelpers, state.TagHelpers);
|
||||
|
||||
Assert.NotSame(original.Documents[Documents[1].FilePath], state.Documents[Documents[1].FilePath]);
|
||||
Assert.NotSame(original.Documents[Documents[1].FilePath], state.Documents[Documents[1].FilePath]);
|
||||
Assert.NotSame(original.Documents[Documents[2].FilePath], state.Documents[Documents[2].FilePath]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectState_WithWorkspaceProject_CallsWorkspaceProjectChangeOnDocumentState()
|
||||
{
|
||||
// Arrange
|
||||
var callCount = 0;
|
||||
|
||||
var documents = new Dictionary<string, DocumentState>();
|
||||
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;
|
||||
|
||||
var changed = WorkspaceProject.WithAssemblyName("Test1");
|
||||
|
||||
// Act
|
||||
var state = original.WithWorkspaceProject(changed);
|
||||
|
||||
// Assert
|
||||
Assert.NotEqual(original.Version, state.Version);
|
||||
Assert.Equal(2, callCount);
|
||||
}
|
||||
|
||||
private class TestDocumentState : DocumentState
|
||||
{
|
||||
public static TestDocumentState Create(
|
||||
HostWorkspaceServices services,
|
||||
HostDocument hostDocument,
|
||||
Func<Task<TextAndVersion>> loader = null,
|
||||
Action onTextChange = null,
|
||||
Action onTextLoaderChange = null,
|
||||
Action onConfigurationChange = null,
|
||||
Action onWorkspaceProjectChange = null)
|
||||
{
|
||||
return new TestDocumentState(services, hostDocument, null, null, loader, onTextChange, onTextLoaderChange, onConfigurationChange, onWorkspaceProjectChange);
|
||||
}
|
||||
|
||||
Action _onTextChange;
|
||||
Action _onTextLoaderChange;
|
||||
Action _onConfigurationChange;
|
||||
Action _onWorkspaceProjectChange;
|
||||
|
||||
private TestDocumentState(
|
||||
HostWorkspaceServices services,
|
||||
HostDocument hostDocument,
|
||||
SourceText text,
|
||||
VersionStamp? version,
|
||||
Func<Task<TextAndVersion>> loader,
|
||||
Action onTextChange,
|
||||
Action onTextLoaderChange,
|
||||
Action onConfigurationChange,
|
||||
Action onWorkspaceProjectChange)
|
||||
: base(services, hostDocument, text, version, loader)
|
||||
{
|
||||
_onTextChange = onTextChange;
|
||||
_onTextLoaderChange = onTextLoaderChange;
|
||||
_onConfigurationChange = onConfigurationChange;
|
||||
_onWorkspaceProjectChange = onWorkspaceProjectChange;
|
||||
}
|
||||
|
||||
public override DocumentState WithText(SourceText sourceText, VersionStamp version)
|
||||
{
|
||||
_onTextChange?.Invoke();
|
||||
return base.WithText(sourceText, version);
|
||||
}
|
||||
|
||||
public override DocumentState WithTextLoader(Func<Task<TextAndVersion>> loader)
|
||||
{
|
||||
_onTextLoaderChange?.Invoke();
|
||||
return base.WithTextLoader(loader);
|
||||
}
|
||||
|
||||
public override DocumentState WithConfigurationChange()
|
||||
{
|
||||
_onConfigurationChange?.Invoke();
|
||||
return base.WithConfigurationChange();
|
||||
}
|
||||
|
||||
public override DocumentState WithWorkspaceProjectChange()
|
||||
{
|
||||
_onWorkspaceProjectChange?.Invoke();
|
||||
return base.WithWorkspaceProjectChange();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,103 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using Microsoft.VisualStudio.Test;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor.Documents
|
||||
{
|
||||
public class EditorDocumentManagerListenerTest
|
||||
{
|
||||
public EditorDocumentManagerListenerTest()
|
||||
{
|
||||
ProjectFilePath = "C:\\project1\\project.csproj";
|
||||
DocumentFilePath = "c:\\project1\\file1.cshtml";
|
||||
TextLoader = TextLoader.From(TextAndVersion.Create(SourceText.From("FILE"), VersionStamp.Default));
|
||||
FileChangeTracker = new DefaultFileChangeTracker(DocumentFilePath);
|
||||
|
||||
TextBuffer = new TestTextBuffer(new StringTextSnapshot("Hello"));
|
||||
}
|
||||
|
||||
private string ProjectFilePath { get; }
|
||||
|
||||
private string DocumentFilePath { get; }
|
||||
|
||||
private TextLoader TextLoader { get; }
|
||||
|
||||
private FileChangeTracker FileChangeTracker { get; }
|
||||
|
||||
private TestTextBuffer TextBuffer { get; }
|
||||
|
||||
[Fact]
|
||||
public void ProjectManager_Changed_DocumentAdded_InvokesGetOrCreateDocument()
|
||||
{
|
||||
// Arrange
|
||||
var changedOnDisk = new EventHandler((o, args) => { });
|
||||
var changedInEditor = new EventHandler((o, args) => { });
|
||||
var opened = new EventHandler((o, args) => { });
|
||||
var closed = new EventHandler((o, args) => { });
|
||||
|
||||
var editorDocumentManger = new Mock<EditorDocumentManager>(MockBehavior.Strict);
|
||||
editorDocumentManger
|
||||
.Setup(e => e.GetOrCreateDocument(It.IsAny<DocumentKey>(), It.IsAny<EventHandler>(), It.IsAny<EventHandler>(), It.IsAny<EventHandler>(), It.IsAny<EventHandler>()))
|
||||
.Returns(GetEditorDocument())
|
||||
.Callback<DocumentKey, EventHandler, EventHandler, EventHandler, EventHandler>((key, onChangedOnDisk, onChangedInEditor, onOpened, onClosed) =>
|
||||
{
|
||||
Assert.Same(changedOnDisk, onChangedOnDisk);
|
||||
Assert.Same(changedInEditor, onChangedInEditor);
|
||||
Assert.Same(opened, onOpened);
|
||||
Assert.Same(closed, onClosed);
|
||||
});
|
||||
|
||||
var listener = new EditorDocumentManagerListener(editorDocumentManger.Object, changedOnDisk, changedInEditor, opened, closed);
|
||||
|
||||
// Act & Assert
|
||||
listener.ProjectManager_Changed(null, new ProjectChangeEventArgs("/Path/to/project.csproj", ProjectChangeKind.DocumentAdded));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectManager_Changed_OpenDocumentAdded_InvokesOnOpened()
|
||||
{
|
||||
// Arrange
|
||||
var called = false;
|
||||
var opened = new EventHandler((o, args) => { called = true; });
|
||||
|
||||
var editorDocumentManger = new Mock<EditorDocumentManager>(MockBehavior.Strict);
|
||||
editorDocumentManger
|
||||
.Setup(e => e.GetOrCreateDocument(It.IsAny<DocumentKey>(), It.IsAny<EventHandler>(), It.IsAny<EventHandler>(), It.IsAny<EventHandler>(), It.IsAny<EventHandler>()))
|
||||
.Returns(GetEditorDocument(isOpen: true));
|
||||
|
||||
var listener = new EditorDocumentManagerListener(editorDocumentManger.Object, onChangedOnDisk: null, onChangedInEditor: null, onOpened: opened, onClosed: null);
|
||||
|
||||
// Act
|
||||
listener.ProjectManager_Changed(null, new ProjectChangeEventArgs("/Path/to/project.csproj", ProjectChangeKind.DocumentAdded));
|
||||
|
||||
// Assert
|
||||
Assert.True(called);
|
||||
}
|
||||
|
||||
private EditorDocument GetEditorDocument(bool isOpen = false)
|
||||
{
|
||||
var document = new EditorDocument(
|
||||
Mock.Of<EditorDocumentManager>(),
|
||||
ProjectFilePath,
|
||||
DocumentFilePath,
|
||||
TextLoader,
|
||||
FileChangeTracker,
|
||||
isOpen ? TextBuffer : null,
|
||||
changedOnDisk: null,
|
||||
changedInEditor: null,
|
||||
opened: null,
|
||||
closed: null);
|
||||
|
||||
return document;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -81,32 +81,5 @@ namespace Microsoft.VisualStudio.Editor.Razor.Documents
|
|||
Assert.Null(document.EditorTextContainer);
|
||||
}
|
||||
}
|
||||
|
||||
private class TestSourceTextContainer : SourceTextContainer
|
||||
{
|
||||
public override event EventHandler<TextChangeEventArgs> TextChanged;
|
||||
|
||||
private SourceText _currentText;
|
||||
|
||||
public TestSourceTextContainer()
|
||||
: this(SourceText.From(string.Empty))
|
||||
{
|
||||
}
|
||||
|
||||
public TestSourceTextContainer(SourceText text)
|
||||
{
|
||||
_currentText = text;
|
||||
}
|
||||
|
||||
public override SourceText CurrentText => _currentText;
|
||||
|
||||
public void PushChange(SourceText text)
|
||||
{
|
||||
var args = new TextChangeEventArgs(_currentText, text);
|
||||
_currentText = text;
|
||||
|
||||
TextChanged?.Invoke(this, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue