Add generated documents to workspace
This commit is contained in:
parent
b486c5a233
commit
13e13d7632
|
|
@ -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.
|
||||
|
||||
// Temporary code until this gets merged into Roslyn
|
||||
#if DOCUMENT_SERVICE_FACTORY
|
||||
namespace Microsoft.CodeAnalysis.Experiment
|
||||
{
|
||||
public interface IDocumentServiceFactory
|
||||
{
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -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.
|
||||
|
||||
// Temporary code until this gets merged into Roslyn
|
||||
#if DOCUMENT_SERVICE_FACTORY
|
||||
namespace Microsoft.CodeAnalysis.Experiment
|
||||
{
|
||||
public interface ISpanMapper
|
||||
{
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
// Temporary code until this gets merged into Roslyn
|
||||
#if DOCUMENT_SERVICE_FACTORY
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Experiment
|
||||
{
|
||||
public class SpanMapResult
|
||||
{
|
||||
public SpanMapResult(Document document, LinePositionSpan linePositionSpan)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
<Description>Razor is a markup syntax for adding server-side logic to web pages. This package contains the Razor design-time infrastructure.</Description>
|
||||
<TargetFrameworks>net46;netstandard2.0</TargetFrameworks>
|
||||
<EnableApiCheck>false</EnableApiCheck>
|
||||
<DefineConstants>$(DefineConstants);DOCUMENT_SERVICE_FACTORY</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -65,6 +65,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
public HostWorkspaceServices Services { get; }
|
||||
|
||||
public GeneratedCodeContainer GeneratedCodeContainer => HostDocument.GeneratedCodeContainer;
|
||||
|
||||
public DocumentGeneratedOutputTracker GeneratedOutput
|
||||
{
|
||||
get
|
||||
|
|
|
|||
|
|
@ -0,0 +1,143 @@
|
|||
// 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.Text;
|
||||
using Microsoft.CodeAnalysis.Experiment;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using System.Collections.Immutable;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal class GeneratedCodeContainer : IDocumentServiceFactory, ISpanMapper
|
||||
{
|
||||
private readonly TextContainer _textContainer;
|
||||
|
||||
public GeneratedCodeContainer()
|
||||
{
|
||||
_textContainer = new TextContainer();
|
||||
}
|
||||
|
||||
public SourceText Source { get; private set; }
|
||||
|
||||
public VersionStamp SourceVersion { get; private set; }
|
||||
|
||||
public RazorCSharpDocument Output { get; private set; }
|
||||
|
||||
public SourceTextContainer SourceTextContainer => _textContainer;
|
||||
|
||||
public TService GetService<TService>()
|
||||
{
|
||||
if (this is TService service)
|
||||
{
|
||||
return service;
|
||||
}
|
||||
|
||||
return default(TService);
|
||||
}
|
||||
|
||||
public void SetOutput(SourceText source, RazorCodeDocument codeDocument)
|
||||
{
|
||||
Source = source;
|
||||
Output = codeDocument.GetCSharpDocument();
|
||||
|
||||
_textContainer.SetText(SourceText.From(Output.GeneratedCode));
|
||||
}
|
||||
|
||||
public Task<ImmutableArray<SpanMapResult>> MapSpansAsync(
|
||||
Document document,
|
||||
IEnumerable<TextSpan> spans,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (Output == null)
|
||||
{
|
||||
return Task.FromResult(ImmutableArray<SpanMapResult>.Empty);
|
||||
}
|
||||
|
||||
var results = ImmutableArray.CreateBuilder<SpanMapResult>();
|
||||
foreach (var span in spans)
|
||||
{
|
||||
if (TryGetLinePositionSpan(span, out var linePositionSpan))
|
||||
{
|
||||
results.Add(new SpanMapResult(document, linePositionSpan));
|
||||
}
|
||||
}
|
||||
|
||||
return Task.FromResult(results.ToImmutable());
|
||||
}
|
||||
|
||||
// Internal for testing.
|
||||
internal bool TryGetLinePositionSpan(TextSpan span, out LinePositionSpan linePositionSpan)
|
||||
{
|
||||
for (var i = 0; i < Output.SourceMappings.Count; i++)
|
||||
{
|
||||
var mapping = Output.SourceMappings[i];
|
||||
if (span.Length > mapping.GeneratedSpan.Length)
|
||||
{
|
||||
// If the length of the generated span is smaller they can't match. A C# expression
|
||||
// won't cover multiple generated spans.
|
||||
//
|
||||
// This heuristic is useful in the Razor context to filter out zero-length
|
||||
// spans.
|
||||
continue;
|
||||
}
|
||||
|
||||
var original = mapping.OriginalSpan.AsTextSpan();
|
||||
var generated = mapping.GeneratedSpan.AsTextSpan();
|
||||
|
||||
var leftOffset = span.Start - generated.Start;
|
||||
var rightOffset = span.End - generated.End;
|
||||
if (leftOffset >= 0 && rightOffset <= 0)
|
||||
{
|
||||
// This span mapping contains the span.
|
||||
var adjusted = new TextSpan(original.Start + leftOffset, (original.End + rightOffset) - (original.Start + leftOffset));
|
||||
linePositionSpan = Source.Lines.GetLinePositionSpan(adjusted);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
linePositionSpan = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
private class TextContainer : SourceTextContainer
|
||||
{
|
||||
public override event EventHandler<TextChangeEventArgs> TextChanged;
|
||||
|
||||
private SourceText _currentText;
|
||||
|
||||
public TextContainer()
|
||||
: this(SourceText.From(string.Empty))
|
||||
{
|
||||
}
|
||||
|
||||
public TextContainer(SourceText sourceText)
|
||||
{
|
||||
if (sourceText == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(sourceText));
|
||||
}
|
||||
|
||||
_currentText = sourceText;
|
||||
}
|
||||
|
||||
public override SourceText CurrentText => _currentText;
|
||||
|
||||
public void SetText(SourceText sourceText)
|
||||
{
|
||||
if (sourceText == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(sourceText));
|
||||
}
|
||||
|
||||
var e = new TextChangeEventArgs(_currentText, sourceText);
|
||||
_currentText = sourceText;
|
||||
|
||||
TextChanged?.Invoke(this, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,10 +21,13 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
FilePath = filePath;
|
||||
TargetPath = targetPath;
|
||||
GeneratedCodeContainer = new GeneratedCodeContainer();
|
||||
}
|
||||
|
||||
public string FilePath { get; }
|
||||
|
||||
public string TargetPath { get; }
|
||||
|
||||
public GeneratedCodeContainer GeneratedCodeContainer { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
internal static class SourceSpanExtensions
|
||||
{
|
||||
public static TextSpan AsTextSpan(this SourceSpan sourceSpan)
|
||||
{
|
||||
return new TextSpan(sourceSpan.AbsoluteIndex, sourceSpan.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -43,6 +43,9 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)Rules\RazorGeneral.xaml">
|
||||
<Context>Project</Context>
|
||||
</PropertyPageSchema>
|
||||
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)Rules\RazorGenerateWithTargetPath.xaml">
|
||||
<Context>Project</Context>
|
||||
</PropertyPageSchema>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="RazorGenerateDesignTime" DependsOnTargets="ResolveRazorGenerateInputs;AssignRazorGenerateTargetPaths" Returns="@(RazorGenerateWithTargetPath)">
|
||||
|
|
|
|||
|
|
@ -11,14 +11,13 @@ using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
|||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
// Deliberately not exported for now, until this feature is working end to end.
|
||||
// [Export(typeof(ProjectSnapshotChangeTrigger))]
|
||||
[Export(typeof(ProjectSnapshotChangeTrigger))]
|
||||
internal class BackgroundDocumentGenerator : ProjectSnapshotChangeTrigger
|
||||
{
|
||||
private ForegroundDispatcher _foregroundDispatcher;
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
private ProjectSnapshotManagerBase _projectManager;
|
||||
|
||||
private readonly Dictionary<DocumentKey, DocumentSnapshot> _files;
|
||||
private readonly Dictionary<DocumentKey, DocumentSnapshot> _work;
|
||||
private Timer _timer;
|
||||
|
||||
[ImportingConstructor]
|
||||
|
|
@ -30,17 +29,16 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
}
|
||||
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
|
||||
_files = new Dictionary<DocumentKey, DocumentSnapshot>();
|
||||
_work = new Dictionary<DocumentKey, DocumentSnapshot>();
|
||||
}
|
||||
|
||||
public bool HasPendingNotifications
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_files)
|
||||
lock (_work)
|
||||
{
|
||||
return _files.Count > 0;
|
||||
return _work.Count > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -134,11 +132,11 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
lock (_files)
|
||||
lock (_work)
|
||||
{
|
||||
// We only want to store the last 'seen' version of any given document. That way when we pick one to process
|
||||
// it's always the best version to use.
|
||||
_files[new DocumentKey(project.FilePath, document.FilePath)] = document;
|
||||
_work[new DocumentKey(project.FilePath, document.FilePath)] = document;
|
||||
|
||||
StartWorker();
|
||||
}
|
||||
|
|
@ -165,18 +163,18 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
|
||||
OnStartingBackgroundWork();
|
||||
|
||||
DocumentSnapshot[] work;
|
||||
lock (_files)
|
||||
KeyValuePair<DocumentKey, DocumentSnapshot>[] work;
|
||||
lock (_work)
|
||||
{
|
||||
work = _files.Values.ToArray();
|
||||
_files.Clear();
|
||||
work = _work.ToArray();
|
||||
_work.Clear();
|
||||
}
|
||||
|
||||
OnBackgroundCapturedWorkload();
|
||||
|
||||
for (var i = 0; i < work.Length; i++)
|
||||
{
|
||||
var document = work[i];
|
||||
var document = work[i].Value;
|
||||
try
|
||||
{
|
||||
await ProcessDocument(document);
|
||||
|
|
@ -189,14 +187,20 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
|
||||
OnCompletingBackgroundWork();
|
||||
|
||||
lock (_files)
|
||||
await Task.Factory.StartNew(
|
||||
() => ReportUpdates(work),
|
||||
CancellationToken.None,
|
||||
TaskCreationOptions.None,
|
||||
_foregroundDispatcher.ForegroundScheduler);
|
||||
|
||||
lock (_work)
|
||||
{
|
||||
// Resetting the timer allows another batch of work to start.
|
||||
_timer.Dispose();
|
||||
_timer = null;
|
||||
|
||||
// If more work came in while we were running start the worker again.
|
||||
if (_files.Count > 0)
|
||||
if (_work.Count > 0)
|
||||
{
|
||||
StartWorker();
|
||||
}
|
||||
|
|
@ -215,6 +219,22 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
}
|
||||
}
|
||||
|
||||
private void ReportUpdates(KeyValuePair<DocumentKey, DocumentSnapshot>[] work)
|
||||
{
|
||||
for (var i = 0; i < work.Length; i++)
|
||||
{
|
||||
var key = work[i].Key;
|
||||
var document = work[i].Value;
|
||||
|
||||
if (document.TryGetText(out var source) &&
|
||||
document.TryGetGeneratedOutput(out var output))
|
||||
{
|
||||
var container = ((DefaultDocumentSnapshot)document).State.GeneratedCodeContainer;
|
||||
container.SetOutput(source, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ReportError(DocumentSnapshot document, Exception ex)
|
||||
{
|
||||
GC.KeepAlive(Task.Factory.StartNew(
|
||||
|
|
@ -229,22 +249,34 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
switch (e.Kind)
|
||||
{
|
||||
case ProjectChangeKind.ProjectAdded:
|
||||
{
|
||||
var projectSnapshot = _projectManager.GetLoadedProject(e.ProjectFilePath);
|
||||
foreach (var documentFilePath in projectSnapshot.DocumentFilePaths)
|
||||
{
|
||||
Enqueue(projectSnapshot, projectSnapshot.GetDocument(documentFilePath));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ProjectChangeKind.ProjectChanged:
|
||||
{
|
||||
var project = _projectManager.GetLoadedProject(e.ProjectFilePath);
|
||||
foreach (var documentFilePath in project.DocumentFilePaths)
|
||||
var projectSnapshot = _projectManager.GetLoadedProject(e.ProjectFilePath);
|
||||
foreach (var documentFilePath in projectSnapshot.DocumentFilePaths)
|
||||
{
|
||||
Enqueue(project, project.GetDocument(documentFilePath));
|
||||
Enqueue(projectSnapshot, projectSnapshot.GetDocument(documentFilePath));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ProjectChangeKind.ProjectRemoved:
|
||||
// ignore
|
||||
break;
|
||||
|
||||
case ProjectChangeKind.DocumentAdded:
|
||||
{
|
||||
var project = _projectManager.GetLoadedProject(e.ProjectFilePath);
|
||||
Enqueue(project, project.GetDocument(e.DocumentFilePath));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ProjectChangeKind.DocumentChanged:
|
||||
{
|
||||
var project = _projectManager.GetLoadedProject(e.ProjectFilePath);
|
||||
|
|
@ -253,10 +285,12 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
break;
|
||||
}
|
||||
|
||||
|
||||
case ProjectChangeKind.ProjectRemoved:
|
||||
case ProjectChangeKind.DocumentRemoved:
|
||||
// ignore
|
||||
break;
|
||||
{
|
||||
// ignore
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException($"Unknown ProjectChangeKind {e.Kind}");
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
<Description>Razor is a markup syntax for adding server-side logic to web pages. This package contains the Razor design-time infrastructure for Visual Studio.</Description>
|
||||
<EnableApiCheck>false</EnableApiCheck>
|
||||
<RulesDirectory>..\Microsoft.NET.Sdk.Razor\build\netstandard2.0\Rules\</RulesDirectory>
|
||||
<DefineConstants>$(DefineConstants);WORKSPACE_PROJECT_CONTEXT_FACTORY</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
|
||||
|
|
|
|||
|
|
@ -7,15 +7,22 @@ using System.Collections.Immutable;
|
|||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks.Dataflow;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.VisualStudio;
|
||||
using Microsoft.VisualStudio.LanguageServices;
|
||||
using Microsoft.VisualStudio.ProjectSystem;
|
||||
using Microsoft.VisualStudio.ProjectSystem.Properties;
|
||||
using Microsoft.VisualStudio.TextManager.Interop;
|
||||
using Item = System.Collections.Generic.KeyValuePair<string, System.Collections.Immutable.IImmutableDictionary<string, string>>;
|
||||
|
||||
#if WORKSPACE_PROJECT_CONTEXT_FACTORY
|
||||
using IWorkspaceProjectContextFactory = Microsoft.VisualStudio.LanguageServices.ProjectSystem.IWorkspaceProjectContextFactory2;
|
||||
#endif
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
// Somewhat similar to https://github.com/dotnet/project-system/blob/fa074d228dcff6dae9e48ce43dd4a3a5aa22e8f0/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/LanguageServiceHost.cs
|
||||
|
|
@ -23,16 +30,18 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// This class is responsible for intializing the Razor ProjectSnapshotManager for cases where
|
||||
// MSBuild provides configuration support (>= 2.1).
|
||||
[AppliesTo("DotNetCoreRazor & DotNetCoreRazorConfiguration")]
|
||||
[ExportVsProfferedProjectService(typeof(IVsContainedLanguageProjectNameProvider))]
|
||||
[Export(ExportContractNames.Scopes.UnconfiguredProject, typeof(IProjectDynamicLoadComponent))]
|
||||
internal class DefaultRazorProjectHost : RazorProjectHostBase
|
||||
internal class DefaultRazorProjectHost : RazorProjectHostBase, IVsContainedLanguageProjectNameProvider
|
||||
{
|
||||
private IDisposable _subscription;
|
||||
|
||||
[ImportingConstructor]
|
||||
public DefaultRazorProjectHost(
|
||||
IUnconfiguredProjectCommonServices commonServices,
|
||||
[Import(typeof(VisualStudioWorkspace))] Workspace workspace)
|
||||
: base(commonServices, workspace)
|
||||
[Import(typeof(VisualStudioWorkspace))] Workspace workspace,
|
||||
Lazy<IWorkspaceProjectContextFactory> projectContextFactory)
|
||||
: base(commonServices, workspace, projectContextFactory)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -68,6 +77,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
Rules.RazorConfiguration.SchemaName,
|
||||
Rules.RazorExtension.SchemaName,
|
||||
Rules.RazorGenerateWithTargetPath.SchemaName,
|
||||
ManagedProjectSystemSchema.CompilerCommandLineArgs.SchemaName,
|
||||
ManagedProjectSystemSchema.ConfigurationGeneral.SchemaName,
|
||||
ManagedProjectSystemSchema.ResolvedCompilationReference.SchemaName,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -107,9 +119,14 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
var documents = GetCurrentDocuments(update.Value);
|
||||
var changedDocuments = GetChangedAndRemovedDocuments(update.Value);
|
||||
|
||||
var references = GetReferences(update.Value);
|
||||
TryGetCommandLineOptions(update.Value.CurrentState, out var commandLineOptions);
|
||||
|
||||
await UpdateAsync(() =>
|
||||
{
|
||||
UpdateProjectUnsafe(hostProject);
|
||||
UpdateWorkspaceProjectOptionsUnsafe(commandLineOptions);
|
||||
UpdateWorkspaceProjectReferencesUnsafe(references);
|
||||
|
||||
for (var i = 0; i < changedDocuments.Length; i++)
|
||||
{
|
||||
|
|
@ -302,6 +319,77 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
// This is temporary code for initializing the companion project. We expect
|
||||
// this to be provided by the Managed Project System in the near future.
|
||||
internal static bool TryGetReferences(
|
||||
IImmutableDictionary<string, IProjectRuleSnapshot> state,
|
||||
out string[] references)
|
||||
{
|
||||
if (!state.TryGetValue(ManagedProjectSystemSchema.ResolvedCompilationReference.ItemName, out var rule))
|
||||
{
|
||||
references = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var items = rule.Items;
|
||||
var referencesList = new List<string>();
|
||||
foreach (var item in items)
|
||||
{
|
||||
var reference = item.Key;
|
||||
if (!referencesList.Contains(reference, FilePathComparer.Instance))
|
||||
{
|
||||
referencesList.Add(reference);
|
||||
}
|
||||
}
|
||||
|
||||
references = referencesList.ToArray();
|
||||
return true;
|
||||
}
|
||||
|
||||
// This is temporary code for initializing the companion project. We expect
|
||||
// this to be provided by the Managed Project System in the near future.
|
||||
internal static bool TryGetCommandLineOptions(
|
||||
IImmutableDictionary<string, IProjectRuleSnapshot> state,
|
||||
out string commandLineOptions)
|
||||
{
|
||||
if (!state.TryGetValue(ManagedProjectSystemSchema.CompilerCommandLineArgs.ItemName, out var rule))
|
||||
{
|
||||
commandLineOptions = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
commandLineOptions = string.Join(" ", rule.Items.Select(kvp => kvp.Key));
|
||||
return true;
|
||||
}
|
||||
|
||||
// This is temporary code for initializing the companion project. We expect
|
||||
// this to be provided by the Managed Project System in the near future.
|
||||
internal static bool TryGetTargetPath(
|
||||
IImmutableDictionary<string, IProjectRuleSnapshot> state,
|
||||
out string targetPath)
|
||||
{
|
||||
if (!state.TryGetValue(ManagedProjectSystemSchema.ConfigurationGeneral.SchemaName, out var rule))
|
||||
{
|
||||
targetPath = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!rule.Properties.TryGetValue(ManagedProjectSystemSchema.ConfigurationGeneral.TargetPathPropertyName, out targetPath))
|
||||
{
|
||||
targetPath = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(targetPath))
|
||||
{
|
||||
targetPath = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private HostDocument[] GetCurrentDocuments(IProjectSubscriptionUpdate update)
|
||||
{
|
||||
if (!update.CurrentState.TryGetValue(Rules.RazorGenerateWithTargetPath.SchemaName, out var rule))
|
||||
|
|
@ -348,5 +436,37 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
return documents.ToArray();
|
||||
}
|
||||
|
||||
// This is temporary code for initializing the companion project. We expect
|
||||
// this to be provided by the Managed Project System in the near future.
|
||||
private string[] GetReferences(IProjectSubscriptionUpdate update)
|
||||
{
|
||||
if (!TryGetReferences(update.CurrentState, out var references))
|
||||
{
|
||||
return Array.Empty<string>();
|
||||
}
|
||||
|
||||
if (TryGetTargetPath(update.CurrentState, out var targetPath))
|
||||
{
|
||||
references = references.Concat(new[] { targetPath, }).ToArray();
|
||||
}
|
||||
|
||||
return references;
|
||||
}
|
||||
|
||||
// This is temporary code for initializing the companion project. We expect
|
||||
// this to be provided by the Managed Project System in the near future.
|
||||
public int GetProjectName([In] uint itemid, [MarshalAs(UnmanagedType.BStr)] out string pbstrProjectName)
|
||||
{
|
||||
if (Current == null)
|
||||
{
|
||||
pbstrProjectName = null;
|
||||
|
||||
return VSConstants.E_INVALIDARG;
|
||||
}
|
||||
|
||||
pbstrProjectName = Path.GetFileNameWithoutExtension(Current.FilePath) + " (Razor)";
|
||||
return VSConstants.S_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
// Temporary code until we get access to these APIs
|
||||
#if WORKSPACE_PROJECT_CONTEXT_FACTORY
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Experiment;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using Microsoft.VisualStudio.LanguageServices.Implementation.TaskList;
|
||||
using IWorkspaceProjectContextFactory = Microsoft.VisualStudio.LanguageServices.ProjectSystem.IWorkspaceProjectContextFactory2;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.ProjectSystem
|
||||
{
|
||||
[Export(typeof(IWorkspaceProjectContextFactory))]
|
||||
internal class DefaultWorkspaceProjectContextFactory : IWorkspaceProjectContextFactory
|
||||
{
|
||||
public IWorkspaceProjectContext CreateProjectContext(string languageName, string projectDisplayName, string projectFilePath, Guid projectGuid, object hierarchy, string binOutputPath)
|
||||
{
|
||||
return new WorkspaceProjectContext();
|
||||
}
|
||||
|
||||
public IWorkspaceProjectContext CreateProjectContext(string languageName, string projectDisplayName, string projectFilePath, Guid projectGuid, object hierarchy, string binOutputPath, ProjectExternalErrorReporter errorReporter)
|
||||
{
|
||||
return new WorkspaceProjectContext();
|
||||
}
|
||||
|
||||
private class WorkspaceProjectContext : IWorkspaceProjectContext
|
||||
{
|
||||
public string DisplayName { get; set; }
|
||||
public string ProjectFilePath { get; set; }
|
||||
public Guid Guid { get; set; }
|
||||
public bool LastDesignTimeBuildSucceeded { get; set; }
|
||||
public string BinOutputPath { get; set; }
|
||||
|
||||
public void AddAdditionalFile(string filePath, bool isInCurrentContext = true)
|
||||
{
|
||||
}
|
||||
|
||||
public void AddAnalyzerReference(string referencePath)
|
||||
{
|
||||
}
|
||||
|
||||
public void AddMetadataReference(string referencePath, MetadataReferenceProperties properties)
|
||||
{
|
||||
}
|
||||
|
||||
public void AddProjectReference(IWorkspaceProjectContext project, MetadataReferenceProperties properties)
|
||||
{
|
||||
}
|
||||
|
||||
public void AddSourceFile(string filePath, bool isInCurrentContext, IEnumerable<string> folderNames, SourceCodeKind sourceCodeKind)
|
||||
{
|
||||
}
|
||||
|
||||
public void AddSourceFile(string filePath, bool isInCurrentContext = true, IEnumerable<string> folderNames = null, SourceCodeKind sourceCodeKind = SourceCodeKind.Regular, IDocumentServiceFactory documentServiceFactory = null)
|
||||
{
|
||||
}
|
||||
|
||||
public void AddSourceFile(string filePath, SourceTextContainer container, bool isInCurrentContext = true, IEnumerable<string> folderNames = null, SourceCodeKind sourceCodeKind = SourceCodeKind.Regular, IDocumentServiceFactory documentServiceFactory = null)
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public void RemoveAdditionalFile(string filePath)
|
||||
{
|
||||
}
|
||||
|
||||
public void RemoveAnalyzerReference(string referencePath)
|
||||
{
|
||||
}
|
||||
|
||||
public void RemoveMetadataReference(string referencePath)
|
||||
{
|
||||
}
|
||||
|
||||
public void RemoveProjectReference(IWorkspaceProjectContext project)
|
||||
{
|
||||
}
|
||||
|
||||
public void RemoveSourceFile(string filePath)
|
||||
{
|
||||
}
|
||||
|
||||
public void SetOptions(string commandLineForOptions)
|
||||
{
|
||||
}
|
||||
|
||||
public void SetRuleSetFile(string filePath)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -38,7 +38,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public FallbackRazorProjectHost(
|
||||
IUnconfiguredProjectCommonServices commonServices,
|
||||
[Import(typeof(VisualStudioWorkspace))] Workspace workspace)
|
||||
: base(commonServices, workspace)
|
||||
: base(commonServices, workspace, projectContextFactory: null)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
// Temporary code until we get access to these APIs
|
||||
#if WORKSPACE_PROJECT_CONTEXT_FACTORY
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Experiment;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.ProjectSystem
|
||||
{
|
||||
internal interface IWorkspaceProjectContext : IDisposable
|
||||
{
|
||||
// Project properties.
|
||||
string DisplayName { get; set; }
|
||||
string ProjectFilePath { get; set; }
|
||||
Guid Guid { get; set; }
|
||||
bool LastDesignTimeBuildSucceeded { get; set; }
|
||||
string BinOutputPath { get; set; }
|
||||
|
||||
// Options.
|
||||
void SetOptions(string commandLineForOptions);
|
||||
|
||||
// References.
|
||||
void AddMetadataReference(string referencePath, MetadataReferenceProperties properties);
|
||||
void RemoveMetadataReference(string referencePath);
|
||||
void AddProjectReference(IWorkspaceProjectContext project, MetadataReferenceProperties properties);
|
||||
void RemoveProjectReference(IWorkspaceProjectContext project);
|
||||
void AddAnalyzerReference(string referencePath);
|
||||
void RemoveAnalyzerReference(string referencePath);
|
||||
|
||||
// Files.
|
||||
void AddSourceFile(string filePath, bool isInCurrentContext, IEnumerable<string> folderNames, SourceCodeKind sourceCodeKind); // This overload just for binary compat with existing code
|
||||
void AddSourceFile(string filePath, bool isInCurrentContext = true, IEnumerable<string> folderNames = null, SourceCodeKind sourceCodeKind = SourceCodeKind.Regular, IDocumentServiceFactory documentServiceFactory = null);
|
||||
void AddSourceFile(string filePath, SourceTextContainer container, bool isInCurrentContext = true, IEnumerable<string> folderNames = null, SourceCodeKind sourceCodeKind = SourceCodeKind.Regular, IDocumentServiceFactory documentServiceFactory = null);
|
||||
|
||||
void RemoveSourceFile(string filePath);
|
||||
void AddAdditionalFile(string filePath, bool isInCurrentContext = true);
|
||||
void RemoveAdditionalFile(string filePath);
|
||||
void SetRuleSetFile(string filePath);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -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.
|
||||
|
||||
// Temporary code until we get access to these APIs
|
||||
#if WORKSPACE_PROJECT_CONTEXT_FACTORY
|
||||
|
||||
using System;
|
||||
using Microsoft.VisualStudio.LanguageServices.Implementation.TaskList;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.ProjectSystem
|
||||
{
|
||||
// The 2 is needed here to prevent clashes with the real interface. MEF is based on the FullName
|
||||
// as a string.
|
||||
internal interface IWorkspaceProjectContextFactory2
|
||||
{
|
||||
IWorkspaceProjectContext CreateProjectContext(string languageName, string projectDisplayName, string projectFilePath, Guid projectGuid, object hierarchy, string binOutputPath);
|
||||
|
||||
|
||||
IWorkspaceProjectContext CreateProjectContext(string languageName, string projectDisplayName, string projectFilePath, Guid projectGuid, object hierarchy, string binOutputPath, ProjectExternalErrorReporter errorReporter);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -6,6 +6,20 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// Well-Known Schema and property names defined by the ManagedProjectSystem
|
||||
internal static class ManagedProjectSystemSchema
|
||||
{
|
||||
public static class CompilerCommandLineArgs
|
||||
{
|
||||
public static readonly string SchemaName = "CompilerCommandLineArgs";
|
||||
|
||||
public static readonly string ItemName = "CompilerCommandLineArgs";
|
||||
}
|
||||
|
||||
public static class ConfigurationGeneral
|
||||
{
|
||||
public static readonly string SchemaName = "ConfigurationGeneral";
|
||||
|
||||
public static readonly string TargetPathPropertyName = "TargetPath";
|
||||
}
|
||||
|
||||
public static class ResolvedCompilationReference
|
||||
{
|
||||
public static readonly string SchemaName = "ResolvedCompilationReference";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
// Temporary code until we get access to these APIs
|
||||
#if WORKSPACE_PROJECT_CONTEXT_FACTORY
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Implementation.TaskList
|
||||
{
|
||||
internal class ProjectExternalErrorReporter
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -5,27 +5,40 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.Composition;
|
||||
using Microsoft.VisualStudio.LanguageServices;
|
||||
using Microsoft.VisualStudio.LanguageServices.ProjectSystem;
|
||||
using Microsoft.VisualStudio.ProjectSystem;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
|
||||
#if WORKSPACE_PROJECT_CONTEXT_FACTORY
|
||||
using IWorkspaceProjectContextFactory = Microsoft.VisualStudio.LanguageServices.ProjectSystem.IWorkspaceProjectContextFactory2;
|
||||
#endif
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal abstract class RazorProjectHostBase : OnceInitializedOnceDisposedAsync, IProjectDynamicLoadComponent
|
||||
{
|
||||
private readonly Workspace _workspace;
|
||||
private readonly Lazy<IWorkspaceProjectContextFactory> _projectContextFactory;
|
||||
private readonly AsyncSemaphore _lock;
|
||||
|
||||
private ProjectSnapshotManagerBase _projectManager;
|
||||
private HostProject _current;
|
||||
private IWorkspaceProjectContext _projectContext;
|
||||
private Dictionary<string, HostDocument> _currentDocuments;
|
||||
private HashSet<string> _references;
|
||||
private string _commandLineOptions;
|
||||
|
||||
public RazorProjectHostBase(
|
||||
IUnconfiguredProjectCommonServices commonServices,
|
||||
[Import(typeof(VisualStudioWorkspace))] Workspace workspace)
|
||||
[Import(typeof(VisualStudioWorkspace))] Workspace workspace,
|
||||
Lazy<IWorkspaceProjectContextFactory> projectContextFactory)
|
||||
: base(commonServices.ThreadingService.JoinableTaskContext)
|
||||
{
|
||||
if (commonServices == null)
|
||||
|
|
@ -40,9 +53,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
CommonServices = commonServices;
|
||||
_workspace = workspace;
|
||||
_projectContextFactory = projectContextFactory;
|
||||
|
||||
_lock = new AsyncSemaphore(initialCount: 1);
|
||||
_currentDocuments = new Dictionary<string, HostDocument>(FilePathComparer.Instance);
|
||||
_references = new HashSet<string>(FilePathComparer.Instance);
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
|
|
@ -73,6 +88,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
_lock = new AsyncSemaphore(initialCount: 1);
|
||||
_currentDocuments = new Dictionary<string, HostDocument>(FilePathComparer.Instance);
|
||||
_references = new HashSet<string>(FilePathComparer.Instance);
|
||||
}
|
||||
|
||||
protected HostProject Current => _current;
|
||||
|
|
@ -129,6 +145,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
var filePath = CommonServices.UnconfiguredProject.FullPath;
|
||||
UpdateProjectUnsafe(new HostProject(filePath, old.Configuration));
|
||||
UpdateWorkspaceProjectOptionsUnsafe(_commandLineOptions);
|
||||
UpdateWorkspaceProjectReferencesUnsafe(_references.ToArray());
|
||||
|
||||
// This should no-op in the common case, just putting it here for insurance.
|
||||
for (var i = 0; i < oldDocuments.Length; i++)
|
||||
|
|
@ -153,6 +171,12 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
return _projectManager;
|
||||
}
|
||||
|
||||
private IWorkspaceProjectContextFactory GetProjectContextFactory()
|
||||
{
|
||||
CommonServices.ThreadingService.VerifyOnUIThread();
|
||||
return _projectContextFactory?.Value;
|
||||
}
|
||||
|
||||
protected async Task UpdateAsync(Action action)
|
||||
{
|
||||
await CommonServices.ThreadingService.SwitchToUIThread();
|
||||
|
|
@ -174,12 +198,40 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
}
|
||||
else if (_current == null && project != null)
|
||||
{
|
||||
// This is temporary code for initializing the companion project. We expect
|
||||
// this to be provided by the Managed Project System in the near future.
|
||||
var projectContextFactory = GetProjectContextFactory();
|
||||
if (projectContextFactory != null)
|
||||
{
|
||||
var assembly = Assembly.Load("Microsoft.VisualStudio.ProjectSystem.Managed, Version=2.7.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
|
||||
var type = assembly.GetType("Microsoft.VisualStudio.ProjectSystem.LanguageServices.IProjectHostProvider");
|
||||
|
||||
var exportProviderType = CommonServices.UnconfiguredProject.Services.ExportProvider.GetType();
|
||||
var method = exportProviderType.GetMethod(nameof(ExportProvider.GetExportedValue), Array.Empty<Type>()).MakeGenericMethod(type);
|
||||
var export = method.Invoke(CommonServices.UnconfiguredProject.Services.ExportProvider, Array.Empty<object>());
|
||||
var host = new IProjectHostProvider(export);
|
||||
|
||||
var displayName = Path.GetFileNameWithoutExtension(CommonServices.UnconfiguredProject.FullPath) + " (Razor)";
|
||||
_projectContext = projectContextFactory.CreateProjectContext(
|
||||
LanguageNames.CSharp,
|
||||
displayName,
|
||||
CommonServices.UnconfiguredProject.FullPath,
|
||||
Guid.NewGuid(),
|
||||
host.UnconfiguredProjectHostObject.ActiveIntellisenseProjectHostObject,
|
||||
null,
|
||||
null);
|
||||
}
|
||||
|
||||
// END temporary code
|
||||
|
||||
projectManager.HostProjectAdded(project);
|
||||
}
|
||||
else if (_current != null && project == null)
|
||||
{
|
||||
Debug.Assert(_currentDocuments.Count == 0);
|
||||
projectManager.HostProjectRemoved(_current);
|
||||
_projectContext?.Dispose();
|
||||
_projectContext = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -189,6 +241,56 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
_current = project;
|
||||
}
|
||||
|
||||
protected void UpdateWorkspaceProjectOptionsUnsafe(string commandLineOptions)
|
||||
{
|
||||
if (_projectContext == null)
|
||||
{
|
||||
_commandLineOptions = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.Equals(_commandLineOptions, commandLineOptions))
|
||||
{
|
||||
_projectContext.SetOptions(commandLineOptions);
|
||||
_commandLineOptions = commandLineOptions;
|
||||
}
|
||||
}
|
||||
|
||||
protected void UpdateWorkspaceProjectReferencesUnsafe(string[] references)
|
||||
{
|
||||
if (_projectContext == null)
|
||||
{
|
||||
_references.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
var newer = new HashSet<string>(references, FilePathComparer.Instance);
|
||||
var older = new HashSet<string>(_references, FilePathComparer.Instance);
|
||||
|
||||
if (older.SetEquals(newer))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var remove = new HashSet<string>(older, FilePathComparer.Instance);
|
||||
remove.ExceptWith(newer);
|
||||
|
||||
var add = new HashSet<string>(newer, FilePathComparer.Instance);
|
||||
add.ExceptWith(older);
|
||||
|
||||
foreach (var reference in remove)
|
||||
{
|
||||
_references.Remove(reference);
|
||||
_projectContext.RemoveMetadataReference(reference);
|
||||
}
|
||||
|
||||
foreach (var reference in add)
|
||||
{
|
||||
_references.Add(reference);
|
||||
_projectContext.AddMetadataReference(reference, new MetadataReferenceProperties());
|
||||
}
|
||||
}
|
||||
|
||||
protected void AddDocumentUnsafe(HostDocument document)
|
||||
{
|
||||
var projectManager = GetProjectManager();
|
||||
|
|
@ -200,6 +302,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
}
|
||||
|
||||
projectManager.DocumentAdded(_current, document, new FileTextLoader(document.FilePath, null));
|
||||
_projectContext?.AddSourceFile(document.FilePath, document.GeneratedCodeContainer.SourceTextContainer, true, GetFolders(document), SourceCodeKind.Regular, document.GeneratedCodeContainer);
|
||||
_currentDocuments.Add(document.FilePath, document);
|
||||
}
|
||||
|
||||
|
|
@ -207,6 +310,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
var projectManager = GetProjectManager();
|
||||
|
||||
_projectContext?.RemoveSourceFile(document.FilePath);
|
||||
projectManager.DocumentRemoved(_current, document);
|
||||
_currentDocuments.Remove(document.FilePath);
|
||||
}
|
||||
|
|
@ -217,6 +321,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
foreach (var kvp in _currentDocuments)
|
||||
{
|
||||
_projectContext?.RemoveSourceFile(kvp.Value.FilePath);
|
||||
_projectManager.DocumentRemoved(_current, kvp.Value);
|
||||
}
|
||||
|
||||
|
|
@ -249,5 +354,48 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
await OnProjectRenamingAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GetFolders(HostDocument document)
|
||||
{
|
||||
var split = document.TargetPath.Split('/');
|
||||
return split.Take(split.Length - 1);
|
||||
}
|
||||
|
||||
private class IUnconfiguredProjectHostObject
|
||||
{
|
||||
private readonly object _inner;
|
||||
|
||||
public IUnconfiguredProjectHostObject(object inner)
|
||||
{
|
||||
_inner = inner;
|
||||
}
|
||||
|
||||
public object ActiveIntellisenseProjectHostObject
|
||||
{
|
||||
get
|
||||
{
|
||||
return _inner.GetType().GetProperty(nameof(ActiveIntellisenseProjectHostObject)).GetValue(_inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class IProjectHostProvider
|
||||
{
|
||||
private readonly object _inner;
|
||||
|
||||
public IProjectHostProvider(object inner)
|
||||
{
|
||||
_inner = inner;
|
||||
}
|
||||
|
||||
public IUnconfiguredProjectHostObject UnconfiguredProjectHostObject
|
||||
{
|
||||
get
|
||||
{
|
||||
var inner = _inner.GetType().GetProperty(nameof(UnconfiguredProjectHostObject)).GetValue(_inner);
|
||||
return new IUnconfiguredProjectHostObject(inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
// 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.Text;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
public class GeneratedCodeContainerTest
|
||||
{
|
||||
[Fact]
|
||||
public void TryGetLinePositionSpan_SpanWithinSourceMapping_ReturnsTrue()
|
||||
{
|
||||
// Arrange
|
||||
var content = @"
|
||||
@{
|
||||
var x = SomeClass.SomeProperty;
|
||||
}
|
||||
";
|
||||
var sourceText = SourceText.From(content);
|
||||
var codeDocument = GetCodeDocument(content);
|
||||
var generatedCode = codeDocument.GetCSharpDocument().GeneratedCode;
|
||||
|
||||
var container = new GeneratedCodeContainer();
|
||||
container.SetOutput(sourceText, codeDocument);
|
||||
|
||||
// TODO: Make writing these tests a little less manual.
|
||||
// Position of `SomeProperty` in the generated code.
|
||||
var symbol = "SomeProperty";
|
||||
var span = new TextSpan(generatedCode.IndexOf(symbol), symbol.Length);
|
||||
|
||||
// Position of `SomeProperty` in the source code.
|
||||
var expectedLineSpan = new LinePositionSpan(new LinePosition(2, 22), new LinePosition(2, 34));
|
||||
|
||||
// Act
|
||||
var result = container.TryGetLinePositionSpan(span, out var lineSpan);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
Assert.Equal(expectedLineSpan, lineSpan);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetLinePositionSpan_SpanOutsideSourceMapping_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var content = @"
|
||||
@{
|
||||
var x = SomeClass.SomeProperty;
|
||||
}
|
||||
";
|
||||
var sourceText = SourceText.From(content);
|
||||
var codeDocument = GetCodeDocument(content);
|
||||
var generatedCode = codeDocument.GetCSharpDocument().GeneratedCode;
|
||||
|
||||
var container = new GeneratedCodeContainer();
|
||||
container.SetOutput(sourceText, codeDocument);
|
||||
|
||||
// Position of `ExecuteAsync` in the generated code.
|
||||
var symbol = "ExecuteAsync";
|
||||
var span = new TextSpan(generatedCode.IndexOf(symbol), symbol.Length);
|
||||
|
||||
// Act
|
||||
var result = container.TryGetLinePositionSpan(span, out var lineSpan);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
private static RazorCodeDocument GetCodeDocument(string content)
|
||||
{
|
||||
var sourceProjectItem = new TestRazorProjectItem("test.cshtml")
|
||||
{
|
||||
Content = content,
|
||||
};
|
||||
|
||||
var engine = RazorProjectEngine.Create();
|
||||
var codeDocument = engine.ProcessDesignTime(sourceProjectItem);
|
||||
return codeDocument;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue