Track the open/closed state of documents
This change intoduces content changes to our project snapshots. We now know the open/closed state of documents that are initialized by the Razor project system and listen to the correct data source based on whether the file is open in the editor. There are a few other random improvements in here as well like a workaround for the upcoming name change to our OOP client type.
This commit is contained in:
parent
e0e1c39cce
commit
fafdd7e3af
|
|
@ -8,17 +8,17 @@ using System.Linq;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
// Deliberately not exported for now, until this feature is working end to end.
|
||||
// [Export(typeof(ProjectSnapshotChangeTrigger))]
|
||||
internal class BackgroundDocumentGenerator : ProjectSnapshotChangeTrigger
|
||||
{
|
||||
private ForegroundDispatcher _foregroundDispatcher;
|
||||
private ProjectSnapshotManagerBase _projectManager;
|
||||
|
||||
private readonly Dictionary<Key, DocumentSnapshot> _files;
|
||||
private readonly Dictionary<DocumentKey, DocumentSnapshot> _files;
|
||||
private Timer _timer;
|
||||
|
||||
[ImportingConstructor]
|
||||
|
|
@ -31,7 +31,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
|
||||
_files = new Dictionary<Key, DocumentSnapshot>();
|
||||
_files = new Dictionary<DocumentKey, DocumentSnapshot>();
|
||||
}
|
||||
|
||||
public bool HasPendingNotifications
|
||||
|
|
@ -127,7 +127,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
{
|
||||
// We only want to store the last 'seen' version of any given document. That way when we pick one to process
|
||||
// it's always the best version to use.
|
||||
_files.Add(new Key(project.FilePath, document.FilePath), document);
|
||||
_files[new DocumentKey(project.FilePath, document.FilePath)] = document;
|
||||
|
||||
StartWorker();
|
||||
}
|
||||
|
|
@ -217,7 +217,6 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
{
|
||||
case ProjectChangeKind.ProjectAdded:
|
||||
case ProjectChangeKind.ProjectChanged:
|
||||
case ProjectChangeKind.DocumentsChanged:
|
||||
{
|
||||
var project = _projectManager.GetLoadedProject(e.ProjectFilePath);
|
||||
foreach (var documentFilePath in project.DocumentFilePaths)
|
||||
|
|
@ -228,12 +227,21 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
break;
|
||||
}
|
||||
|
||||
case ProjectChangeKind.DocumentContentChanged:
|
||||
case ProjectChangeKind.ProjectRemoved:
|
||||
// ignore
|
||||
break;
|
||||
|
||||
case ProjectChangeKind.DocumentAdded:
|
||||
case ProjectChangeKind.DocumentChanged:
|
||||
{
|
||||
throw null;
|
||||
var project = _projectManager.GetLoadedProject(e.ProjectFilePath);
|
||||
Enqueue(project, project.GetDocument(e.DocumentFilePath));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ProjectChangeKind.ProjectRemoved:
|
||||
|
||||
case ProjectChangeKind.DocumentRemoved:
|
||||
// ignore
|
||||
break;
|
||||
|
||||
|
|
@ -241,38 +249,5 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
throw new InvalidOperationException($"Unknown ProjectChangeKind {e.Kind}");
|
||||
}
|
||||
}
|
||||
|
||||
private struct Key : IEquatable<Key>
|
||||
{
|
||||
public Key(string projectFilePath, string documentFilePath)
|
||||
{
|
||||
ProjectFilePath = projectFilePath;
|
||||
DocumentFilePath = documentFilePath;
|
||||
}
|
||||
|
||||
public string ProjectFilePath { get; }
|
||||
|
||||
public string DocumentFilePath { get; }
|
||||
|
||||
public bool Equals(Key other)
|
||||
{
|
||||
return
|
||||
FilePathComparer.Instance.Equals(ProjectFilePath, other.ProjectFilePath) &&
|
||||
FilePathComparer.Instance.Equals(DocumentFilePath, other.DocumentFilePath);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is Key key ? Equals(key) : false;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hash = new HashCodeCombiner();
|
||||
hash.Add(ProjectFilePath, FilePathComparer.Instance);
|
||||
hash.Add(DocumentFilePath, FilePathComparer.Instance);
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
public struct DocumentKey : IEquatable<DocumentKey>
|
||||
{
|
||||
public DocumentKey(string projectFilePath, string documentFilePath)
|
||||
{
|
||||
ProjectFilePath = projectFilePath;
|
||||
DocumentFilePath = documentFilePath;
|
||||
}
|
||||
|
||||
public string ProjectFilePath { get; }
|
||||
|
||||
public string DocumentFilePath { get; }
|
||||
|
||||
public bool Equals(DocumentKey other)
|
||||
{
|
||||
return
|
||||
FilePathComparer.Instance.Equals(ProjectFilePath, other.ProjectFilePath) &&
|
||||
FilePathComparer.Instance.Equals(DocumentFilePath, other.DocumentFilePath);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is DocumentKey key ? Equals(key) : false;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hash = new HashCodeCombiner();
|
||||
hash.Add(ProjectFilePath, FilePathComparer.Instance);
|
||||
hash.Add(DocumentFilePath, FilePathComparer.Instance);
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,10 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
|
|
@ -33,12 +35,32 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
public override string TargetPath => State.HostDocument.TargetPath;
|
||||
|
||||
public override Task<SourceText> GetTextAsync()
|
||||
{
|
||||
return State.GetTextAsync();
|
||||
}
|
||||
|
||||
public override Task<VersionStamp> GetTextVersionAsync()
|
||||
{
|
||||
return State.GetTextVersionAsync();
|
||||
}
|
||||
|
||||
public override Task<RazorCodeDocument> GetGeneratedOutputAsync()
|
||||
{
|
||||
// IMPORTANT: Don't put more code here. We want this to return a cached task.
|
||||
return State.GeneratedOutput.GetGeneratedOutputInitializationTask(Project, this);
|
||||
}
|
||||
|
||||
public override bool TryGetText(out SourceText result)
|
||||
{
|
||||
return State.TryGetText(out result);
|
||||
}
|
||||
|
||||
public override bool TryGetTextVersion(out VersionStamp result)
|
||||
{
|
||||
return State.TryGetTextVersion(out result);
|
||||
}
|
||||
|
||||
public override bool TryGetGeneratedOutput(out RazorCodeDocument result)
|
||||
{
|
||||
if (State.GeneratedOutput.IsResultAvailable)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
|
|
@ -34,6 +37,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// Each entry holds a ProjectState and an optional ProjectSnapshot. ProjectSnapshots are
|
||||
// created lazily.
|
||||
private readonly Dictionary<string, Entry> _projects;
|
||||
private readonly HashSet<string> _openDocuments;
|
||||
|
||||
public DefaultProjectSnapshotManager(
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
|
|
@ -67,6 +71,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
Workspace = workspace;
|
||||
|
||||
_projects = new Dictionary<string, Entry>(FilePathComparer.Instance);
|
||||
_openDocuments = new HashSet<string>(FilePathComparer.Instance);
|
||||
|
||||
for (var i = 0; i < _triggers.Length; i++)
|
||||
{
|
||||
|
|
@ -133,7 +138,19 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
return GetLoadedProject(filePath) ?? new EphemeralProjectSnapshot(Workspace.Services, filePath);
|
||||
}
|
||||
|
||||
public override void DocumentAdded(HostProject hostProject, HostDocument document)
|
||||
public override bool IsDocumentOpen(string documentFilePath)
|
||||
{
|
||||
if (documentFilePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documentFilePath));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
return _openDocuments.Contains(documentFilePath);
|
||||
}
|
||||
|
||||
public override void DocumentAdded(HostProject hostProject, HostDocument document, TextLoader textLoader)
|
||||
{
|
||||
if (hostProject == null)
|
||||
{
|
||||
|
|
@ -149,13 +166,17 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
if (_projects.TryGetValue(hostProject.FilePath, out var entry))
|
||||
{
|
||||
var state = entry.State.WithAddedHostDocument(document);
|
||||
var loader = textLoader == null ? DocumentState.EmptyLoader : (Func<Task<TextAndVersion>>)(() =>
|
||||
{
|
||||
return textLoader.LoadTextAndVersionAsync(Workspace, null, CancellationToken.None);
|
||||
});
|
||||
var state = entry.State.WithAddedHostDocument(document, loader);
|
||||
|
||||
// Document updates can no-op.
|
||||
if (!object.ReferenceEquals(state, entry.State))
|
||||
{
|
||||
_projects[hostProject.FilePath] = new Entry(state);
|
||||
NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, ProjectChangeKind.DocumentsChanged));
|
||||
NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, document.FilePath, ProjectChangeKind.DocumentAdded));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -181,7 +202,187 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
if (!object.ReferenceEquals(state, entry.State))
|
||||
{
|
||||
_projects[hostProject.FilePath] = new Entry(state);
|
||||
NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, ProjectChangeKind.DocumentsChanged));
|
||||
NotifyListeners(new ProjectChangeEventArgs(hostProject.FilePath, document.FilePath, ProjectChangeKind.DocumentRemoved));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void DocumentOpened(string projectFilePath, string documentFilePath, SourceText sourceText)
|
||||
{
|
||||
if (projectFilePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectFilePath));
|
||||
}
|
||||
|
||||
if (documentFilePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documentFilePath));
|
||||
}
|
||||
|
||||
if (sourceText == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(sourceText));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
if (_projects.TryGetValue(projectFilePath, out var entry) &&
|
||||
entry.State.Documents.TryGetValue(documentFilePath, out var older))
|
||||
{
|
||||
ProjectState state;
|
||||
SourceText olderText;
|
||||
VersionStamp olderVersion;
|
||||
|
||||
var currentText = sourceText;
|
||||
if (older.TryGetText(out olderText) &&
|
||||
older.TryGetTextVersion(out olderVersion))
|
||||
{
|
||||
var version = currentText.ContentEquals(olderText) ? olderVersion : olderVersion.GetNewerVersion();
|
||||
state = entry.State.WithChangedHostDocument(older.HostDocument, currentText, version);
|
||||
}
|
||||
else
|
||||
{
|
||||
state = entry.State.WithChangedHostDocument(older.HostDocument, async () =>
|
||||
{
|
||||
olderText = await older.GetTextAsync().ConfigureAwait(false);
|
||||
olderVersion = await older.GetTextVersionAsync().ConfigureAwait(false);
|
||||
|
||||
var version = currentText.ContentEquals(olderText) ? olderVersion : olderVersion.GetNewerVersion();
|
||||
return TextAndVersion.Create(currentText, version, documentFilePath);
|
||||
});
|
||||
}
|
||||
|
||||
_openDocuments.Add(documentFilePath);
|
||||
|
||||
// Document updates can no-op.
|
||||
if (!object.ReferenceEquals(state, entry.State))
|
||||
{
|
||||
_projects[projectFilePath] = new Entry(state);
|
||||
NotifyListeners(new ProjectChangeEventArgs(projectFilePath, documentFilePath, ProjectChangeKind.DocumentChanged));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void DocumentClosed(string projectFilePath, string documentFilePath, TextLoader textLoader)
|
||||
{
|
||||
if (projectFilePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectFilePath));
|
||||
}
|
||||
|
||||
if (documentFilePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documentFilePath));
|
||||
}
|
||||
|
||||
if (textLoader == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(textLoader));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
if (_projects.TryGetValue(projectFilePath, out var entry) &&
|
||||
entry.State.Documents.TryGetValue(documentFilePath, out var older))
|
||||
{
|
||||
var state = entry.State.WithChangedHostDocument(older.HostDocument, async () =>
|
||||
{
|
||||
return await textLoader.LoadTextAndVersionAsync(Workspace, default, default);
|
||||
});
|
||||
|
||||
_openDocuments.Remove(documentFilePath);
|
||||
|
||||
// Document updates can no-op.
|
||||
if (!object.ReferenceEquals(state, entry.State))
|
||||
{
|
||||
_projects[projectFilePath] = new Entry(state);
|
||||
NotifyListeners(new ProjectChangeEventArgs(projectFilePath, documentFilePath, ProjectChangeKind.DocumentChanged));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void DocumentChanged(string projectFilePath, string documentFilePath, SourceText sourceText)
|
||||
{
|
||||
if (projectFilePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectFilePath));
|
||||
}
|
||||
|
||||
if (documentFilePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documentFilePath));
|
||||
}
|
||||
|
||||
if (sourceText == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(sourceText));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
if (_projects.TryGetValue(projectFilePath, out var entry) &&
|
||||
entry.State.Documents.TryGetValue(documentFilePath, out var older))
|
||||
{
|
||||
ProjectState state;
|
||||
SourceText olderText;
|
||||
VersionStamp olderVersion;
|
||||
|
||||
var currentText = sourceText;
|
||||
if (older.TryGetText(out olderText) &&
|
||||
older.TryGetTextVersion(out olderVersion))
|
||||
{
|
||||
var version = currentText.ContentEquals(olderText) ? olderVersion : olderVersion.GetNewerVersion();
|
||||
state = entry.State.WithChangedHostDocument(older.HostDocument, currentText, version);
|
||||
}
|
||||
else
|
||||
{
|
||||
state = entry.State.WithChangedHostDocument(older.HostDocument, async () =>
|
||||
{
|
||||
olderText = await older.GetTextAsync().ConfigureAwait(false);
|
||||
olderVersion = await older.GetTextVersionAsync().ConfigureAwait(false);
|
||||
|
||||
var version = currentText.ContentEquals(olderText) ? olderVersion : olderVersion.GetNewerVersion();
|
||||
return TextAndVersion.Create(currentText, version, documentFilePath);
|
||||
});
|
||||
}
|
||||
|
||||
// Document updates can no-op.
|
||||
if (!object.ReferenceEquals(state, entry.State))
|
||||
{
|
||||
_projects[projectFilePath] = new Entry(state);
|
||||
NotifyListeners(new ProjectChangeEventArgs(projectFilePath, documentFilePath, ProjectChangeKind.DocumentChanged));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void DocumentChanged(string projectFilePath, string documentFilePath, TextLoader textLoader)
|
||||
{
|
||||
if (projectFilePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectFilePath));
|
||||
}
|
||||
|
||||
if (documentFilePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documentFilePath));
|
||||
}
|
||||
|
||||
if (textLoader == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(textLoader));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
if (_projects.TryGetValue(projectFilePath, out var entry) &&
|
||||
entry.State.Documents.TryGetValue(documentFilePath, out var older))
|
||||
{
|
||||
var state = entry.State.WithChangedHostDocument(older.HostDocument, async () =>
|
||||
{
|
||||
return await textLoader.LoadTextAndVersionAsync(Workspace, default, default);
|
||||
});
|
||||
|
||||
// Document updates can no-op.
|
||||
if (!object.ReferenceEquals(state, entry.State))
|
||||
{
|
||||
_projects[projectFilePath] = new Entry(state);
|
||||
NotifyListeners(new ProjectChangeEventArgs(projectFilePath, documentFilePath, ProjectChangeKind.DocumentChanged));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -205,7 +406,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// So if possible find a WorkspaceProject.
|
||||
var workspaceProject = GetWorkspaceProject(hostProject.FilePath);
|
||||
|
||||
var state = new ProjectState(Workspace.Services, hostProject, workspaceProject);
|
||||
var state = ProjectState.Create(Workspace.Services, hostProject, workspaceProject);
|
||||
_projects[hostProject.FilePath] = new Entry(state);
|
||||
|
||||
// We need to notify listeners about every project add.
|
||||
|
|
@ -300,7 +501,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// We also need to check the projectId here. If this is a multi-targeting project then we are only interested
|
||||
// in a single workspace project. Just use the one that showed up first.
|
||||
if (_projects.TryGetValue(workspaceProject.FilePath, out var entry) &&
|
||||
(entry.State.WorkspaceProject == null || entry.State.WorkspaceProject.Id == workspaceProject.Id))
|
||||
(entry.State.WorkspaceProject == null || entry.State.WorkspaceProject.Id == workspaceProject.Id) &&
|
||||
(entry.State.WorkspaceProject == null || entry.State.WorkspaceProject.Version.GetNewerVersion(workspaceProject.Version) == workspaceProject.Version))
|
||||
{
|
||||
var state = entry.State.WithWorkspaceProject(workspaceProject);
|
||||
|
||||
|
|
|
|||
|
|
@ -10,11 +10,6 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
internal class DocumentGeneratedOutputTracker
|
||||
{
|
||||
// Don't keep anything if the configuration has changed. It's OK if the
|
||||
// workspace project has changes, we'll consider whether the tag helpers are
|
||||
// difference before reusing a previous result.
|
||||
private const ProjectDifference Mask = ProjectDifference.ConfigurationChanged;
|
||||
|
||||
private readonly object _lock;
|
||||
|
||||
private DocumentGeneratedOutputTracker _older;
|
||||
|
|
@ -31,6 +26,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
public bool IsResultAvailable => _task?.IsCompleted == true;
|
||||
|
||||
public DocumentGeneratedOutputTracker Older => _older;
|
||||
|
||||
public Task<RazorCodeDocument> GetGeneratedOutputInitializationTask(ProjectSnapshot project, DocumentSnapshot document)
|
||||
{
|
||||
if (project == null)
|
||||
|
|
@ -57,18 +54,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
return _task;
|
||||
}
|
||||
|
||||
public DocumentGeneratedOutputTracker ForkFor(DocumentState state, ProjectDifference difference)
|
||||
public DocumentGeneratedOutputTracker Fork()
|
||||
{
|
||||
if (state == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(state));
|
||||
}
|
||||
|
||||
if ((difference & Mask) != 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new DocumentGeneratedOutputTracker(this);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
|
|
@ -12,8 +14,16 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
public abstract string TargetPath { get; }
|
||||
|
||||
public abstract Task<SourceText> GetTextAsync();
|
||||
|
||||
public abstract Task<VersionStamp> GetTextVersionAsync();
|
||||
|
||||
public abstract Task<RazorCodeDocument> GetGeneratedOutputAsync();
|
||||
|
||||
public abstract bool TryGetText(out SourceText result);
|
||||
|
||||
public abstract bool TryGetTextVersion(out VersionStamp result);
|
||||
|
||||
public abstract bool TryGetGeneratedOutput(out RazorCodeDocument result);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,17 +2,33 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal class DocumentState
|
||||
{
|
||||
private static readonly TextAndVersion EmptyText = TextAndVersion.Create(
|
||||
SourceText.From(string.Empty),
|
||||
VersionStamp.Default);
|
||||
|
||||
public static readonly Func<Task<TextAndVersion>> EmptyLoader = () => Task.FromResult(EmptyText);
|
||||
|
||||
private readonly object _lock;
|
||||
|
||||
private Func<Task<TextAndVersion>> _loader;
|
||||
private Task<TextAndVersion> _loaderTask;
|
||||
private SourceText _sourceText;
|
||||
private VersionStamp? _version;
|
||||
|
||||
private DocumentGeneratedOutputTracker _generatedOutput;
|
||||
|
||||
public DocumentState(HostWorkspaceServices services, HostDocument hostDocument)
|
||||
public static DocumentState Create(
|
||||
HostWorkspaceServices services,
|
||||
HostDocument hostDocument,
|
||||
Func<Task<TextAndVersion>> loader)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
|
|
@ -24,33 +40,29 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
throw new ArgumentNullException(nameof(hostDocument));
|
||||
}
|
||||
|
||||
Services = services;
|
||||
HostDocument = hostDocument;
|
||||
Version = VersionStamp.Create();
|
||||
|
||||
_lock = new object();
|
||||
loader = loader ?? EmptyLoader;
|
||||
return new DocumentState(services, hostDocument, null, null, loader);
|
||||
}
|
||||
|
||||
public DocumentState(DocumentState previous, ProjectDifference difference)
|
||||
private DocumentState(
|
||||
HostWorkspaceServices services,
|
||||
HostDocument hostDocument,
|
||||
SourceText text,
|
||||
VersionStamp? version,
|
||||
Func<Task<TextAndVersion>> loader)
|
||||
{
|
||||
if (previous == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(previous));
|
||||
}
|
||||
|
||||
Services = previous.Services;
|
||||
HostDocument = previous.HostDocument;
|
||||
Version = previous.Version.GetNewerVersion();
|
||||
|
||||
_generatedOutput = previous._generatedOutput?.ForkFor(this, difference);
|
||||
Services = services;
|
||||
HostDocument = hostDocument;
|
||||
_sourceText = text;
|
||||
_version = version;
|
||||
_loader = loader;
|
||||
_lock = new object();
|
||||
}
|
||||
|
||||
public HostDocument HostDocument { get; }
|
||||
|
||||
public HostWorkspaceServices Services { get; }
|
||||
|
||||
public VersionStamp Version { get; }
|
||||
|
||||
public DocumentGeneratedOutputTracker GeneratedOutput
|
||||
{
|
||||
get
|
||||
|
|
@ -69,5 +81,119 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
return _generatedOutput;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<SourceText> GetTextAsync()
|
||||
{
|
||||
if (TryGetText(out var text))
|
||||
{
|
||||
return text;
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
_loaderTask = _loader();
|
||||
}
|
||||
|
||||
return (await _loaderTask.ConfigureAwait(false)).Text;
|
||||
}
|
||||
|
||||
public async Task<VersionStamp> GetTextVersionAsync()
|
||||
{
|
||||
if (TryGetTextVersion(out var version))
|
||||
{
|
||||
return version;
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
_loaderTask = _loader();
|
||||
}
|
||||
|
||||
return (await _loaderTask.ConfigureAwait(false)).Version;
|
||||
}
|
||||
|
||||
public bool TryGetText(out SourceText result)
|
||||
{
|
||||
if (_sourceText != null)
|
||||
{
|
||||
result = _sourceText;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_loaderTask != null && _loaderTask.IsCompleted)
|
||||
{
|
||||
result = _loaderTask.Result.Text;
|
||||
return true;
|
||||
}
|
||||
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetTextVersion(out VersionStamp result)
|
||||
{
|
||||
if (_version != null)
|
||||
{
|
||||
result = _version.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_loaderTask != null && _loaderTask.IsCompleted)
|
||||
{
|
||||
result = _loaderTask.Result.Version;
|
||||
return true;
|
||||
}
|
||||
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public DocumentState WithConfigurationChange()
|
||||
{
|
||||
var state = new DocumentState(Services, HostDocument, _sourceText, _version, _loader);
|
||||
|
||||
// The source could not have possibly changed.
|
||||
state._sourceText = _sourceText;
|
||||
state._version = _version;
|
||||
state._loaderTask = _loaderTask;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
public DocumentState WithWorkspaceProjectChange()
|
||||
{
|
||||
var state = new DocumentState(Services, HostDocument, _sourceText, _version, _loader);
|
||||
|
||||
// The source could not have possibly changed.
|
||||
state._sourceText = _sourceText;
|
||||
state._version = _version;
|
||||
state._loaderTask = _loaderTask;
|
||||
|
||||
// Opportunistically cache the generated code
|
||||
state._generatedOutput = _generatedOutput?.Fork();
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
public DocumentState WithText(SourceText sourceText, VersionStamp version)
|
||||
{
|
||||
if (sourceText == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(sourceText));
|
||||
}
|
||||
|
||||
return new DocumentState(Services, HostDocument, sourceText, version, null);
|
||||
}
|
||||
|
||||
|
||||
public DocumentState WithTextLoader(Func<Task<TextAndVersion>> loader)
|
||||
{
|
||||
if (loader == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(loader));
|
||||
}
|
||||
|
||||
return new DocumentState(Services, HostDocument, null, null, loader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,10 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal class HostDocument : IEquatable<HostDocument>
|
||||
internal class HostDocument
|
||||
{
|
||||
public HostDocument(string filePath, string targetPath)
|
||||
{
|
||||
|
|
@ -27,35 +26,5 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public string FilePath { get; }
|
||||
|
||||
public string TargetPath { get; }
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return base.Equals(obj as DocumentSnapshot);
|
||||
}
|
||||
|
||||
public bool Equals(DocumentSnapshot other)
|
||||
{
|
||||
if (ReferenceEquals(null, other))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return
|
||||
FilePathComparer.Instance.Equals(FilePath, other.FilePath) &&
|
||||
FilePathComparer.Instance.Equals(TargetPath, other.TargetPath);
|
||||
}
|
||||
|
||||
public bool Equals(HostDocument other)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hash = new HashCodeCombiner();
|
||||
hash.Add(FilePath, FilePathComparer.Instance);
|
||||
hash.Add(TargetPath, FilePathComparer.Instance);
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,12 +9,31 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
public ProjectChangeEventArgs(string projectFilePath, ProjectChangeKind kind)
|
||||
{
|
||||
if (projectFilePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectFilePath));
|
||||
}
|
||||
|
||||
ProjectFilePath = projectFilePath;
|
||||
Kind = kind;
|
||||
}
|
||||
|
||||
public ProjectChangeEventArgs(string projectFilePath, string documentFilePath, ProjectChangeKind kind)
|
||||
{
|
||||
if (projectFilePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectFilePath));
|
||||
}
|
||||
|
||||
ProjectFilePath = projectFilePath;
|
||||
DocumentFilePath = documentFilePath;
|
||||
Kind = kind;
|
||||
}
|
||||
|
||||
public string ProjectFilePath { get; }
|
||||
|
||||
public string DocumentFilePath { get; }
|
||||
|
||||
public ProjectChangeKind Kind { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
ProjectAdded,
|
||||
ProjectRemoved,
|
||||
ProjectChanged,
|
||||
DocumentsChanged,
|
||||
DocumentContentChanged,
|
||||
DocumentAdded,
|
||||
DocumentRemoved,
|
||||
|
||||
// This could be a state change (opened/closed) or a content change.
|
||||
DocumentChanged,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
WorkspaceProjectAdded = 2,
|
||||
WorkspaceProjectRemoved = 4,
|
||||
WorkspaceProjectChanged = 8,
|
||||
DocumentsChanged = 16,
|
||||
DocumentAdded = 16,
|
||||
DocumentRemoved = 32,
|
||||
DocumentChanged = 64,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
public abstract IReadOnlyList<ProjectSnapshot> Projects { get; }
|
||||
|
||||
public abstract bool IsDocumentOpen(string documentFilePath);
|
||||
|
||||
public abstract ProjectSnapshot GetLoadedProject(string filePath);
|
||||
|
||||
public abstract ProjectSnapshot GetOrCreateProject(string filePath);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
|
|
@ -9,7 +10,16 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
public abstract Workspace Workspace { get; }
|
||||
|
||||
public abstract void DocumentAdded(HostProject hostProject, HostDocument hostDocument);
|
||||
public abstract void DocumentAdded(HostProject hostProject, HostDocument hostDocument, TextLoader textLoader);
|
||||
|
||||
// Yeah this is kinda ugly.
|
||||
public abstract void DocumentOpened(string projectFilePath, string documentFilePath, SourceText sourceText);
|
||||
|
||||
public abstract void DocumentClosed(string projectFilePath, string documentFilePath, TextLoader textLoader);
|
||||
|
||||
public abstract void DocumentChanged(string projectFilePath, string documentFilePath, TextLoader textLoader);
|
||||
|
||||
public abstract void DocumentChanged(string projectFilePath, string documentFilePath, SourceText sourceText);
|
||||
|
||||
public abstract void DocumentRemoved(HostProject hostProject, HostDocument hostDocument);
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
|
|
@ -17,10 +19,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
private ProjectEngineTracker _projectEngine;
|
||||
private ProjectTagHelperTracker _tagHelpers;
|
||||
|
||||
public ProjectState(
|
||||
HostWorkspaceServices services,
|
||||
HostProject hostProject,
|
||||
Project workspaceProject)
|
||||
public static ProjectState Create(HostWorkspaceServices services, HostProject hostProject, Project workspaceProject = null)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
|
|
@ -32,6 +31,14 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
throw new ArgumentNullException(nameof(hostProject));
|
||||
}
|
||||
|
||||
return new ProjectState(services, hostProject, workspaceProject);
|
||||
}
|
||||
|
||||
private ProjectState(
|
||||
HostWorkspaceServices services,
|
||||
HostProject hostProject,
|
||||
Project workspaceProject)
|
||||
{
|
||||
Services = services;
|
||||
HostProject = hostProject;
|
||||
WorkspaceProject = workspaceProject;
|
||||
|
|
@ -41,7 +48,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
_lock = new object();
|
||||
}
|
||||
|
||||
public ProjectState(
|
||||
private ProjectState(
|
||||
ProjectState older,
|
||||
ProjectDifference difference,
|
||||
HostProject hostProject,
|
||||
|
|
@ -126,13 +133,18 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
}
|
||||
}
|
||||
|
||||
public ProjectState WithAddedHostDocument(HostDocument hostDocument)
|
||||
public ProjectState WithAddedHostDocument(HostDocument hostDocument, Func<Task<TextAndVersion>> loader)
|
||||
{
|
||||
if (hostDocument == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hostDocument));
|
||||
}
|
||||
|
||||
if (loader == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(loader));
|
||||
}
|
||||
|
||||
// Ignore attempts to 'add' a document with different data, we only
|
||||
// care about one, so it might as well be the one we have.
|
||||
if (Documents.ContainsKey(hostDocument.FilePath))
|
||||
|
|
@ -145,10 +157,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
documents.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
documents.Add(hostDocument.FilePath, DocumentState.Create(Services, hostDocument, loader));
|
||||
|
||||
documents.Add(hostDocument.FilePath, new DocumentState(Services, hostDocument));
|
||||
|
||||
var difference = ProjectDifference.DocumentsChanged;
|
||||
var difference = ProjectDifference.DocumentAdded;
|
||||
var state = new ProjectState(this, difference, HostProject, WorkspaceProject, documents);
|
||||
return state;
|
||||
}
|
||||
|
|
@ -173,11 +185,65 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
documents.Remove(hostDocument.FilePath);
|
||||
|
||||
var difference = ProjectDifference.DocumentsChanged;
|
||||
var difference = ProjectDifference.DocumentRemoved;
|
||||
var state = new ProjectState(this, difference, HostProject, WorkspaceProject, documents);
|
||||
return state;
|
||||
}
|
||||
|
||||
public ProjectState WithChangedHostDocument(HostDocument hostDocument, SourceText sourceText, VersionStamp version)
|
||||
{
|
||||
if (hostDocument == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hostDocument));
|
||||
}
|
||||
|
||||
if (!Documents.ContainsKey(hostDocument.FilePath))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
var documents = new Dictionary<string, DocumentState>(FilePathComparer.Instance);
|
||||
foreach (var kvp in Documents)
|
||||
{
|
||||
documents.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
if (documents.TryGetValue(hostDocument.FilePath, out var document))
|
||||
{
|
||||
documents[hostDocument.FilePath] = document.WithText(sourceText, version);
|
||||
}
|
||||
|
||||
var state = new ProjectState(this, ProjectDifference.DocumentChanged, HostProject, WorkspaceProject, documents);
|
||||
return state;
|
||||
}
|
||||
|
||||
public ProjectState WithChangedHostDocument(HostDocument hostDocument, Func<Task<TextAndVersion>> loader)
|
||||
{
|
||||
if (hostDocument == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hostDocument));
|
||||
}
|
||||
|
||||
if (!Documents.ContainsKey(hostDocument.FilePath))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
var documents = new Dictionary<string, DocumentState>(FilePathComparer.Instance);
|
||||
foreach (var kvp in Documents)
|
||||
{
|
||||
documents.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
if (documents.TryGetValue(hostDocument.FilePath, out var document))
|
||||
{
|
||||
documents[hostDocument.FilePath] = document.WithTextLoader(loader);
|
||||
}
|
||||
|
||||
var state = new ProjectState(this, ProjectDifference.DocumentChanged, HostProject, WorkspaceProject, documents);
|
||||
return state;
|
||||
}
|
||||
|
||||
public ProjectState WithHostProject(HostProject hostProject)
|
||||
{
|
||||
if (hostProject == null)
|
||||
|
|
@ -194,7 +260,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
var documents = new Dictionary<string, DocumentState>(FilePathComparer.Instance);
|
||||
foreach (var kvp in Documents)
|
||||
{
|
||||
documents.Add(kvp.Key, new DocumentState(kvp.Value, difference));
|
||||
documents.Add(kvp.Key, kvp.Value.WithConfigurationChange());
|
||||
}
|
||||
|
||||
var state = new ProjectState(this, difference, hostProject, WorkspaceProject, documents);
|
||||
|
|
@ -227,7 +293,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
var documents = new Dictionary<string, DocumentState>(FilePathComparer.Instance);
|
||||
foreach (var kvp in Documents)
|
||||
{
|
||||
documents.Add(kvp.Key, new DocumentState(kvp.Value, difference));
|
||||
documents.Add(kvp.Key, kvp.Value.WithConfigurationChange());
|
||||
}
|
||||
|
||||
var state = new ProjectState(this, difference, HostProject, workspaceProject, documents);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Composition;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
|
|
@ -11,11 +13,21 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
private ProjectSnapshotManagerBase _projectManager;
|
||||
|
||||
public int ProjectChangeDelay { get; set; } = 3 * 1000;
|
||||
|
||||
// We throttle updates to projects to prevent doing too much work while the projects
|
||||
// are being initialized.
|
||||
//
|
||||
// Internal for testing
|
||||
internal Dictionary<ProjectId, Task> _deferredUpdates;
|
||||
|
||||
public override void Initialize(ProjectSnapshotManagerBase projectManager)
|
||||
{
|
||||
_projectManager = projectManager;
|
||||
_projectManager.Workspace.WorkspaceChanged += Workspace_WorkspaceChanged;
|
||||
|
||||
_deferredUpdates = new Dictionary<ProjectId, Task>();
|
||||
|
||||
InitializeSolution(_projectManager.Workspace.CurrentSolution);
|
||||
}
|
||||
|
||||
|
|
@ -47,10 +59,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
case WorkspaceChangeKind.ProjectChanged:
|
||||
case WorkspaceChangeKind.ProjectReloaded:
|
||||
{
|
||||
project = e.NewSolution.GetProject(e.ProjectId);
|
||||
Debug.Assert(project != null);
|
||||
|
||||
_projectManager.WorkspaceProjectChanged(project);
|
||||
EnqueueUpdate(e.ProjectId);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -81,5 +90,27 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void EnqueueUpdate(ProjectId projectId)
|
||||
{
|
||||
// A race is not possible here because we use the main thread to synchronize the updates
|
||||
// by capturing the sync context.
|
||||
if (!_deferredUpdates.TryGetValue(projectId, out var update) || update.IsCompleted)
|
||||
{
|
||||
_deferredUpdates[projectId] = UpdateAfterDelay(projectId);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateAfterDelay(ProjectId projectId)
|
||||
{
|
||||
await Task.Delay(ProjectChangeDelay);
|
||||
|
||||
var solution = _projectManager.Workspace.CurrentSolution;
|
||||
var workspaceProject = solution.GetProject(projectId);
|
||||
if (workspaceProject != null)
|
||||
{
|
||||
_projectManager.WorkspaceProjectChanged(workspaceProject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using System.Runtime.CompilerServices;
|
|||
[assembly: InternalsVisibleTo("Microsoft.CodeAnalysis.Razor.Workspaces, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.CodeAnalysis.Razor.Workspaces.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.CodeAnalysis.Remote.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.Editor.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.LanguageServices.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.RazorExtension, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
|
||||
|
|
|
|||
|
|
@ -292,7 +292,9 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
|
||||
switch (e.Kind)
|
||||
{
|
||||
case ProjectChangeKind.DocumentsChanged:
|
||||
case ProjectChangeKind.DocumentAdded:
|
||||
case ProjectChangeKind.DocumentRemoved:
|
||||
case ProjectChangeKind.DocumentChanged:
|
||||
|
||||
// Nothing to do.
|
||||
break;
|
||||
|
|
@ -311,11 +313,6 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
OnContextChanged(ContextChangeKind.ProjectChanged);
|
||||
break;
|
||||
|
||||
case ProjectChangeKind.DocumentContentChanged:
|
||||
|
||||
// Do nothing
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException($"Unknown ProjectChangeKind {e.Kind}");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,162 @@
|
|||
// 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.Text;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor.Documents
|
||||
{
|
||||
// Tracks the mutable state associated with a document - in contrast to DocumentSnapshot
|
||||
// which tracks the state at a point in time.
|
||||
internal sealed class EditorDocument : IDisposable
|
||||
{
|
||||
private readonly EditorDocumentManager _documentManager;
|
||||
private readonly FileChangeTracker _fileTracker;
|
||||
private readonly SnapshotChangeTracker _snapshotTracker;
|
||||
private readonly EventHandler _changedOnDisk;
|
||||
private readonly EventHandler _changedInEditor;
|
||||
private readonly EventHandler _opened;
|
||||
private readonly EventHandler _closed;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public EditorDocument(
|
||||
EditorDocumentManager documentManager,
|
||||
string projectFilePath,
|
||||
string documentFilePath,
|
||||
TextLoader textLoader,
|
||||
FileChangeTracker fileTracker,
|
||||
ITextBuffer textBuffer,
|
||||
EventHandler changedOnDisk,
|
||||
EventHandler changedInEditor,
|
||||
EventHandler opened,
|
||||
EventHandler closed)
|
||||
{
|
||||
if (documentManager == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documentManager));
|
||||
}
|
||||
|
||||
if (projectFilePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectFilePath));
|
||||
}
|
||||
|
||||
if (documentFilePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documentFilePath));
|
||||
}
|
||||
|
||||
if (textLoader == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(textLoader));
|
||||
}
|
||||
|
||||
if (fileTracker == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(fileTracker));
|
||||
}
|
||||
|
||||
_documentManager = documentManager;
|
||||
ProjectFilePath = projectFilePath;
|
||||
DocumentFilePath = documentFilePath;
|
||||
TextLoader = textLoader;
|
||||
_fileTracker = fileTracker;
|
||||
_changedOnDisk = changedOnDisk;
|
||||
_changedInEditor = changedInEditor;
|
||||
_opened = opened;
|
||||
_closed = closed;
|
||||
|
||||
_snapshotTracker = new SnapshotChangeTracker();
|
||||
_fileTracker.Changed += ChangeTracker_Changed;
|
||||
|
||||
// Only one of these should be active at a time.
|
||||
if (textBuffer == null)
|
||||
{
|
||||
_fileTracker.StartListening();
|
||||
}
|
||||
else
|
||||
{
|
||||
_snapshotTracker.StartTracking(textBuffer);
|
||||
|
||||
EditorTextBuffer = textBuffer;
|
||||
EditorTextContainer = textBuffer.AsTextContainer();
|
||||
EditorTextContainer.TextChanged += TextContainer_Changed;
|
||||
}
|
||||
}
|
||||
|
||||
public string ProjectFilePath { get; }
|
||||
|
||||
public string DocumentFilePath { get; }
|
||||
|
||||
public bool IsOpenInEditor => EditorTextBuffer != null;
|
||||
|
||||
public SourceTextContainer EditorTextContainer { get; private set; }
|
||||
|
||||
public ITextBuffer EditorTextBuffer { get; private set; }
|
||||
|
||||
public TextLoader TextLoader { get; }
|
||||
|
||||
public void ProcessOpen(ITextBuffer textBuffer)
|
||||
{
|
||||
if (textBuffer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(textBuffer));
|
||||
}
|
||||
|
||||
_fileTracker.StopListening();
|
||||
|
||||
_snapshotTracker.StartTracking(textBuffer);
|
||||
EditorTextBuffer = textBuffer;
|
||||
EditorTextContainer = textBuffer.AsTextContainer();
|
||||
EditorTextContainer.TextChanged += TextContainer_Changed;
|
||||
|
||||
_opened?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public void ProcessClose()
|
||||
{
|
||||
_closed?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
_snapshotTracker.StopTracking(EditorTextBuffer);
|
||||
|
||||
EditorTextContainer.TextChanged -= TextContainer_Changed;
|
||||
EditorTextContainer = null;
|
||||
EditorTextBuffer = null;
|
||||
|
||||
_fileTracker.StartListening();
|
||||
}
|
||||
|
||||
private void ChangeTracker_Changed(object sender, FileChangeEventArgs e)
|
||||
{
|
||||
if (e.Kind == FileChangeKind.Changed)
|
||||
{
|
||||
_changedOnDisk?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private void TextContainer_Changed(object sender, TextChangeEventArgs e)
|
||||
{
|
||||
_changedInEditor?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
_fileTracker.Changed -= ChangeTracker_Changed;
|
||||
_fileTracker.StopListening();
|
||||
|
||||
EditorTextContainer.TextChanged -= TextContainer_Changed;
|
||||
EditorTextContainer = null;
|
||||
EditorTextBuffer = null;
|
||||
|
||||
_documentManager.RemoveDocument(this);
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor.Documents
|
||||
{
|
||||
internal abstract class EditorDocumentManager : IWorkspaceService
|
||||
{
|
||||
public abstract EditorDocument GetOrCreateDocument(
|
||||
DocumentKey key,
|
||||
EventHandler changedOnDisk,
|
||||
EventHandler changedInEditor,
|
||||
EventHandler opened,
|
||||
EventHandler closed);
|
||||
|
||||
public abstract bool TryGetDocument(DocumentKey key, out EditorDocument document);
|
||||
|
||||
public abstract bool TryGetMatchingDocuments(string filePath, out EditorDocument[] documents);
|
||||
|
||||
public abstract void RemoveDocument(EditorDocument document);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,221 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor.Documents
|
||||
{
|
||||
// Similar to the DocumentProvider in dotnet/Roslyn - but simplified quite a bit to remove
|
||||
// concepts that we don't need. Responsible for providing data about text changes for documents
|
||||
// and editor open/closed state.
|
||||
internal abstract class EditorDocumentManagerBase : EditorDocumentManager
|
||||
{
|
||||
private readonly FileChangeTrackerFactory _fileChangeTrackerFactory;
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
|
||||
private readonly Dictionary<DocumentKey, EditorDocument> _documents;
|
||||
private readonly Dictionary<string, List<DocumentKey>> _documentsByFilePath;
|
||||
protected readonly object _lock;
|
||||
|
||||
public EditorDocumentManagerBase(
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
FileChangeTrackerFactory fileChangeTrackerFactory)
|
||||
{
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
if (fileChangeTrackerFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(fileChangeTrackerFactory));
|
||||
}
|
||||
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
_fileChangeTrackerFactory = fileChangeTrackerFactory;
|
||||
|
||||
_documents = new Dictionary<DocumentKey, EditorDocument>();
|
||||
_documentsByFilePath = new Dictionary<string, List<DocumentKey>>(FilePathComparer.Instance);
|
||||
_lock = new object();
|
||||
}
|
||||
|
||||
protected ForegroundDispatcher ForegroundDispatcher => _foregroundDispatcher;
|
||||
|
||||
protected abstract ITextBuffer GetTextBufferForOpenDocument(string filePath);
|
||||
|
||||
protected abstract void OnDocumentOpened(EditorDocument document);
|
||||
|
||||
protected abstract void OnDocumentClosed(EditorDocument document);
|
||||
|
||||
public sealed override bool TryGetDocument(DocumentKey key, out EditorDocument document)
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
return _documents.TryGetValue(key, out document);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed override bool TryGetMatchingDocuments(string filePath, out EditorDocument[] documents)
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (!_documentsByFilePath.TryGetValue(filePath, out var keys))
|
||||
{
|
||||
documents = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
documents = new EditorDocument[keys.Count];
|
||||
for (var i = 0; i < keys.Count; i++)
|
||||
{
|
||||
documents[i] = _documents[keys[i]];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed override EditorDocument GetOrCreateDocument(
|
||||
DocumentKey key,
|
||||
EventHandler changedOnDisk,
|
||||
EventHandler changedInEditor,
|
||||
EventHandler opened,
|
||||
EventHandler closed)
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
EditorDocument document;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (TryGetDocument(key, out document))
|
||||
{
|
||||
return document;
|
||||
}
|
||||
|
||||
// Check if the document is already open and initialized, and associate a buffer if possible.
|
||||
var textBuffer = GetTextBufferForOpenDocument(key.DocumentFilePath);
|
||||
document = new EditorDocument(
|
||||
this,
|
||||
key.ProjectFilePath,
|
||||
key.DocumentFilePath,
|
||||
new FileTextLoader(key.DocumentFilePath, defaultEncoding: null),
|
||||
_fileChangeTrackerFactory.Create(key.DocumentFilePath),
|
||||
textBuffer,
|
||||
changedOnDisk,
|
||||
changedInEditor,
|
||||
opened,
|
||||
closed);
|
||||
|
||||
_documents.Add(key, document);
|
||||
|
||||
if (!_documentsByFilePath.TryGetValue(key.DocumentFilePath, out var documents))
|
||||
{
|
||||
documents = new List<DocumentKey>();
|
||||
_documentsByFilePath.Add(key.DocumentFilePath, documents);
|
||||
}
|
||||
|
||||
if (!documents.Contains(key))
|
||||
{
|
||||
documents.Add(key);
|
||||
}
|
||||
|
||||
if (document.IsOpenInEditor)
|
||||
{
|
||||
OnDocumentOpened(document);
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
}
|
||||
|
||||
protected void DocumentOpened(string filePath, ITextBuffer textBuffer)
|
||||
{
|
||||
if (filePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(filePath));
|
||||
}
|
||||
|
||||
if (textBuffer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(textBuffer));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (TryGetMatchingDocuments(filePath, out var documents))
|
||||
{
|
||||
for (var i = 0; i < documents.Length; i++)
|
||||
{
|
||||
var document = documents[i];
|
||||
|
||||
document.ProcessOpen(textBuffer);
|
||||
OnDocumentOpened(document);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void DocumentClosed(string filePath)
|
||||
{
|
||||
if (filePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(filePath));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (TryGetMatchingDocuments(filePath, out var documents))
|
||||
{
|
||||
for (var i = 0; i < documents.Length; i++)
|
||||
{
|
||||
var document = documents[i];
|
||||
|
||||
document.ProcessClose();
|
||||
OnDocumentClosed(document);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed override void RemoveDocument(EditorDocument document)
|
||||
{
|
||||
if (document == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(document));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
var key = new DocumentKey(document.ProjectFilePath, document.DocumentFilePath);
|
||||
if (_documentsByFilePath.TryGetValue(document.DocumentFilePath, out var documents))
|
||||
{
|
||||
documents.Remove(key);
|
||||
|
||||
if (documents.Count == 0)
|
||||
{
|
||||
_documentsByFilePath.Remove(document.DocumentFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
_documents.Remove(key);
|
||||
|
||||
if (document.IsOpenInEditor)
|
||||
{
|
||||
OnDocumentClosed(document);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Composition;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor.Documents
|
||||
{
|
||||
// Hooks up the document manager to project snapshot events. The project snapshot manager
|
||||
// tracks the existance of projects/files and the the document manager watches for changes.
|
||||
//
|
||||
// This class forwards notifications in both directions.
|
||||
[Export(typeof(ProjectSnapshotChangeTrigger))]
|
||||
internal class EditorDocumentManagerListener : ProjectSnapshotChangeTrigger
|
||||
{
|
||||
private readonly EventHandler _onChangedOnDisk;
|
||||
private readonly EventHandler _onChangedInEditor;
|
||||
private readonly EventHandler _onOpened;
|
||||
private readonly EventHandler _onClosed;
|
||||
|
||||
private EditorDocumentManager _documentManager;
|
||||
private ProjectSnapshotManagerBase _projectManager;
|
||||
|
||||
[ImportingConstructor]
|
||||
public EditorDocumentManagerListener()
|
||||
{
|
||||
_onChangedOnDisk = Document_ChangedOnDisk;
|
||||
_onChangedInEditor = Document_ChangedInEditor;
|
||||
_onOpened = Document_Opened;
|
||||
_onClosed = Document_Closed;
|
||||
}
|
||||
|
||||
public override void Initialize(ProjectSnapshotManagerBase projectManager)
|
||||
{
|
||||
if (projectManager == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectManager));
|
||||
}
|
||||
|
||||
_projectManager = projectManager;
|
||||
_documentManager = projectManager.Workspace.Services.GetRequiredService<EditorDocumentManager>();
|
||||
|
||||
_projectManager.Changed += ProjectManager_Changed;
|
||||
}
|
||||
|
||||
private 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);
|
||||
if (document.IsOpenInEditor)
|
||||
{
|
||||
Document_Opened(document, EventArgs.Empty);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ProjectChangeKind.DocumentRemoved:
|
||||
{
|
||||
// This class 'owns' the document entry so it's safe for us to dispose it.
|
||||
if (_documentManager.TryGetDocument(new DocumentKey(e.ProjectFilePath, e.DocumentFilePath), out var document))
|
||||
{
|
||||
document.Dispose();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Document_ChangedOnDisk(object sender, EventArgs e)
|
||||
{
|
||||
var document = (EditorDocument)sender;
|
||||
_projectManager.DocumentChanged(document.ProjectFilePath, document.DocumentFilePath, document.TextLoader);
|
||||
}
|
||||
|
||||
private void Document_ChangedInEditor(object sender, EventArgs e)
|
||||
{
|
||||
var document = (EditorDocument)sender;
|
||||
_projectManager.DocumentChanged(document.ProjectFilePath, document.DocumentFilePath, document.EditorTextContainer.CurrentText);
|
||||
}
|
||||
|
||||
private void Document_Opened(object sender, EventArgs e)
|
||||
{
|
||||
var document = (EditorDocument)sender;
|
||||
_projectManager.DocumentOpened(document.ProjectFilePath, document.DocumentFilePath, document.EditorTextContainer.CurrentText);
|
||||
}
|
||||
|
||||
private void Document_Closed(object sender, EventArgs e)
|
||||
{
|
||||
var document = (EditorDocument)sender;
|
||||
_projectManager.DocumentClosed(document.ProjectFilePath, document.DocumentFilePath, document.TextLoader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.VisualStudio.Text;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor.Documents
|
||||
{
|
||||
// See ReiteratedVersionSnapshotTracker in dotnet/Roslyn -- this is primarily here for the
|
||||
// side-effect of making sure the last 'reiterated' snapshot is retained in memory.
|
||||
//
|
||||
// Since we're interacting with the workspace in the same way, we're doing the same thing.
|
||||
internal class SnapshotChangeTracker
|
||||
{
|
||||
private ITextBuffer _textBuffer;
|
||||
private ITextSnapshot _snapshot;
|
||||
|
||||
public void StartTracking(ITextBuffer buffer)
|
||||
{
|
||||
// buffer has changed. stop tracking old buffer
|
||||
if (_textBuffer != null && buffer != _textBuffer)
|
||||
{
|
||||
_textBuffer.ChangedHighPriority -= OnTextBufferChanged;
|
||||
|
||||
_textBuffer = null;
|
||||
_snapshot = null;
|
||||
}
|
||||
|
||||
// start tracking new buffer
|
||||
if (buffer != null && _snapshot == null)
|
||||
{
|
||||
_snapshot = buffer.CurrentSnapshot;
|
||||
_textBuffer = buffer;
|
||||
|
||||
buffer.ChangedHighPriority += OnTextBufferChanged;
|
||||
}
|
||||
}
|
||||
|
||||
public void StopTracking(ITextBuffer buffer)
|
||||
{
|
||||
if (_textBuffer == buffer && buffer != null && _snapshot != null)
|
||||
{
|
||||
buffer.ChangedHighPriority -= OnTextBufferChanged;
|
||||
|
||||
_textBuffer = null;
|
||||
_snapshot = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTextBufferChanged(object sender, TextContentChangedEventArgs e)
|
||||
{
|
||||
if (sender is ITextBuffer buffer)
|
||||
{
|
||||
var snapshot = _snapshot;
|
||||
if (snapshot != null && snapshot.Version != null && e.AfterVersion != null &&
|
||||
snapshot.Version.ReiteratedVersionNumber < e.AfterVersion.ReiteratedVersionNumber)
|
||||
{
|
||||
_snapshot = e.After;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
// 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.VisualStudio.Shell.Interop;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor.Documents
|
||||
{
|
||||
internal class RunningDocumentTableEventSink : IVsRunningDocTableEvents3
|
||||
{
|
||||
private readonly VisualStudioEditorDocumentManager _documentManager;
|
||||
|
||||
public RunningDocumentTableEventSink(VisualStudioEditorDocumentManager documentManager)
|
||||
{
|
||||
if (documentManager == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documentManager));
|
||||
}
|
||||
|
||||
_documentManager = documentManager;
|
||||
}
|
||||
|
||||
public int OnAfterAttributeChangeEx(uint docCookie, uint grfAttribs, IVsHierarchy pHierOld, uint itemidOld, string pszMkDocumentOld, IVsHierarchy pHierNew, uint itemidNew, string pszMkDocumentNew)
|
||||
{
|
||||
// Document has been initialized.
|
||||
if ((grfAttribs & (uint)__VSRDTATTRIB3.RDTA_DocumentInitialized) != 0)
|
||||
{
|
||||
_documentManager.DocumentOpened(docCookie);
|
||||
}
|
||||
|
||||
if ((grfAttribs & (uint)__VSRDTATTRIB.RDTA_MkDocument) != 0)
|
||||
{
|
||||
_documentManager.DocumentRenamed(docCookie, pszMkDocumentOld, pszMkDocumentNew);
|
||||
}
|
||||
|
||||
return VSConstants.S_OK;
|
||||
}
|
||||
|
||||
public int OnBeforeLastDocumentUnlock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining)
|
||||
{
|
||||
// Document is being closed
|
||||
if (dwReadLocksRemaining + dwEditLocksRemaining == 0)
|
||||
{
|
||||
_documentManager.DocumentClosed(docCookie);
|
||||
}
|
||||
|
||||
return VSConstants.S_OK;
|
||||
}
|
||||
|
||||
public int OnBeforeSave(uint docCookie) => VSConstants.S_OK;
|
||||
|
||||
public int OnAfterSave(uint docCookie) => VSConstants.S_OK;
|
||||
|
||||
public int OnAfterAttributeChange(uint docCookie, uint grfAttribs) => VSConstants.S_OK;
|
||||
|
||||
public int OnBeforeDocumentWindowShow(uint docCookie, int fFirstShow, IVsWindowFrame pFrame) => VSConstants.S_OK;
|
||||
|
||||
public int OnAfterDocumentWindowHide(uint docCookie, IVsWindowFrame pFrame) => VSConstants.S_OK;
|
||||
|
||||
public int OnAfterFirstDocumentLock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining) => VSConstants.S_OK;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,254 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.VisualStudio.Shell.Interop;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
using Microsoft.VisualStudio.TextManager.Interop;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor.Documents
|
||||
{
|
||||
// Similar to the DocumentProvider in dotnet/Roslyn - but simplified quite a bit to remove
|
||||
// concepts that we don't need. Responsible for providing data about text changes for documents
|
||||
// and editor open/closed state.
|
||||
internal class VisualStudioEditorDocumentManager : EditorDocumentManagerBase
|
||||
{
|
||||
private readonly IVsEditorAdaptersFactoryService _editorAdaptersFactory;
|
||||
|
||||
private readonly IVsRunningDocumentTable4 _runningDocumentTable;
|
||||
private readonly uint _rdtCookie;
|
||||
|
||||
private readonly Dictionary<uint, List<DocumentKey>> _documentsByCookie;
|
||||
private readonly Dictionary<DocumentKey, uint> _cookiesByDocument;
|
||||
|
||||
public VisualStudioEditorDocumentManager(
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
FileChangeTrackerFactory fileChangeTrackerFactory,
|
||||
IVsRunningDocumentTable runningDocumentTable,
|
||||
IVsEditorAdaptersFactoryService editorAdaptersFactory)
|
||||
: base(foregroundDispatcher, fileChangeTrackerFactory)
|
||||
{
|
||||
if (runningDocumentTable == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(runningDocumentTable));
|
||||
}
|
||||
|
||||
if (editorAdaptersFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(editorAdaptersFactory));
|
||||
}
|
||||
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
if (fileChangeTrackerFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(fileChangeTrackerFactory));
|
||||
}
|
||||
|
||||
_runningDocumentTable = (IVsRunningDocumentTable4)runningDocumentTable;
|
||||
_editorAdaptersFactory = editorAdaptersFactory;
|
||||
|
||||
var hr = runningDocumentTable.AdviseRunningDocTableEvents(new RunningDocumentTableEventSink(this), out _rdtCookie);
|
||||
Marshal.ThrowExceptionForHR(hr);
|
||||
|
||||
_documentsByCookie = new Dictionary<uint, List<DocumentKey>>();
|
||||
_cookiesByDocument = new Dictionary<DocumentKey, uint>();
|
||||
}
|
||||
|
||||
protected override ITextBuffer GetTextBufferForOpenDocument(string filePath)
|
||||
{
|
||||
if (filePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(filePath));
|
||||
}
|
||||
|
||||
// Check if the document is already open and initialized, and associate a buffer if possible.
|
||||
var cookie = VSConstants.VSCOOKIE_NIL;
|
||||
ITextBuffer textBuffer = null;
|
||||
if (_runningDocumentTable.IsMonikerValid(filePath) &&
|
||||
((cookie = _runningDocumentTable.GetDocumentCookie(filePath)) != VSConstants.VSCOOKIE_NIL) &&
|
||||
(_runningDocumentTable.GetDocumentFlags(cookie) & (uint)_VSRDTFLAGS4.RDT_PendingInitialization) == 0)
|
||||
{
|
||||
var vsTextBuffer = ((object)_runningDocumentTable.GetDocumentData(cookie)) as VsTextBuffer;
|
||||
textBuffer = vsTextBuffer == null ? null : _editorAdaptersFactory.GetDocumentBuffer(vsTextBuffer);
|
||||
return textBuffer;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override void OnDocumentOpened(EditorDocument document)
|
||||
{
|
||||
var cookie = _runningDocumentTable.GetDocumentCookie(document.DocumentFilePath);
|
||||
if (cookie != VSConstants.VSCOOKIE_NIL)
|
||||
{
|
||||
TrackOpenDocument(cookie, new DocumentKey(document.ProjectFilePath, document.DocumentFilePath));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDocumentClosed(EditorDocument document)
|
||||
{
|
||||
var key = new DocumentKey(document.ProjectFilePath, document.DocumentFilePath);
|
||||
if (_cookiesByDocument.TryGetValue(key, out var cookie))
|
||||
{
|
||||
UntrackOpenDocument(cookie, key);
|
||||
}
|
||||
}
|
||||
|
||||
public void DocumentOpened(uint cookie)
|
||||
{
|
||||
ForegroundDispatcher.AssertForegroundThread();
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
// Casts avoid dynamic
|
||||
if ((object)(_runningDocumentTable.GetDocumentData(cookie)) is IVsTextBuffer vsTextBuffer)
|
||||
{
|
||||
var filePath = _runningDocumentTable.GetDocumentMoniker(cookie);
|
||||
if (!TryGetMatchingDocuments(filePath, out var documents))
|
||||
{
|
||||
// This isn't a document that we're interesting in.
|
||||
return;
|
||||
}
|
||||
|
||||
var textBuffer = _editorAdaptersFactory.GetDataBuffer(vsTextBuffer);
|
||||
if (textBuffer == null)
|
||||
{
|
||||
// The text buffer has not been created yet, register to be notified when it is.
|
||||
VsTextBufferDataEventsSink.Subscribe(vsTextBuffer, () =>
|
||||
{
|
||||
BufferLoaded(vsTextBuffer, filePath);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// It's possible that events could be fired out of order and that this is a rename.
|
||||
if (_documentsByCookie.ContainsKey(cookie))
|
||||
{
|
||||
DocumentClosed(cookie, exceptFilePath: filePath);
|
||||
}
|
||||
|
||||
BufferLoaded(textBuffer, filePath, documents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void BufferLoaded(IVsTextBuffer vsTextBuffer, string filePath)
|
||||
{
|
||||
ForegroundDispatcher.AssertForegroundThread();
|
||||
|
||||
var textBuffer = _editorAdaptersFactory.GetDocumentBuffer(vsTextBuffer);
|
||||
if (textBuffer != null)
|
||||
{
|
||||
// We potentially waited for the editor to initialize on this code path, so requery
|
||||
// the documents.
|
||||
if (TryGetMatchingDocuments(filePath, out var documents))
|
||||
{
|
||||
BufferLoaded(textBuffer, filePath, documents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void BufferLoaded(ITextBuffer textBuffer, string filePath, EditorDocument[] documents)
|
||||
{
|
||||
ForegroundDispatcher.AssertForegroundThread();
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
for (var i = 0; i < documents.Length; i++)
|
||||
{
|
||||
DocumentOpened(filePath, textBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DocumentClosed(uint cookie, string exceptFilePath = null)
|
||||
{
|
||||
ForegroundDispatcher.AssertForegroundThread();
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (!_documentsByCookie.TryGetValue(cookie, out var documents))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// We have to deal with some complications here due to renames and event ordering and such.
|
||||
// We we might see multiple documents open for a cookie (due to linked files), but only one of them
|
||||
// has been renamed. In that case, we just process the change that we know about.
|
||||
var filePaths = new HashSet<string>(documents.Select(d => d.DocumentFilePath));
|
||||
filePaths.Remove(exceptFilePath);
|
||||
|
||||
foreach (var filePath in filePaths)
|
||||
{
|
||||
DocumentClosed(filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DocumentRenamed(uint cookie, string fromFilePath, string toFilePath)
|
||||
{
|
||||
ForegroundDispatcher.AssertForegroundThread();
|
||||
|
||||
// Ignore changes is casing
|
||||
if (FilePathComparer.Instance.Equals(fromFilePath, toFilePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
// Treat a rename as a close + reopen.
|
||||
//
|
||||
// Due to ordering issues, we could see a partial rename. This is why we need to pass the new
|
||||
// file path here.
|
||||
DocumentClosed(cookie, exceptFilePath: toFilePath);
|
||||
}
|
||||
|
||||
// Try to open any existing documents that match the new name.
|
||||
if ((_runningDocumentTable.GetDocumentFlags(cookie) & (uint)_VSRDTFLAGS4.RDT_PendingInitialization) == 0)
|
||||
{
|
||||
DocumentOpened(cookie);
|
||||
}
|
||||
}
|
||||
|
||||
private void TrackOpenDocument(uint cookie, DocumentKey key)
|
||||
{
|
||||
if (!_documentsByCookie.TryGetValue(cookie, out var documents))
|
||||
{
|
||||
documents = new List<DocumentKey>();
|
||||
_documentsByCookie.Add(cookie, documents);
|
||||
}
|
||||
|
||||
if (!documents.Contains(key))
|
||||
{
|
||||
documents.Add(key);
|
||||
}
|
||||
|
||||
_cookiesByDocument[key] = cookie;
|
||||
}
|
||||
|
||||
private void UntrackOpenDocument(uint cookie, DocumentKey key)
|
||||
{
|
||||
if (_documentsByCookie.TryGetValue(cookie, out var documents))
|
||||
{
|
||||
documents.Remove(key);
|
||||
|
||||
if (documents.Count == 0)
|
||||
{
|
||||
_documentsByCookie.Remove(cookie);
|
||||
}
|
||||
}
|
||||
|
||||
_cookiesByDocument.Remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Composition;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Host.Mef;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.VisualStudio.Shell;
|
||||
using Microsoft.VisualStudio.Shell.Interop;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor.Documents
|
||||
{
|
||||
[Shared]
|
||||
[ExportWorkspaceServiceFactory(typeof(EditorDocumentManager), ServiceLayer.Host)]
|
||||
internal class VisualStudioEditorDocumentManagerFactory : IWorkspaceServiceFactory
|
||||
{
|
||||
private readonly SVsServiceProvider _serviceProvider;
|
||||
private readonly IVsEditorAdaptersFactoryService _editorAdaptersFactory;
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
|
||||
[ImportingConstructor]
|
||||
public VisualStudioEditorDocumentManagerFactory(
|
||||
SVsServiceProvider serviceProvider,
|
||||
IVsEditorAdaptersFactoryService editorAdaptersFactory,
|
||||
ForegroundDispatcher foregroundDispatcher)
|
||||
{
|
||||
if (serviceProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(serviceProvider));
|
||||
}
|
||||
|
||||
if (editorAdaptersFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(editorAdaptersFactory));
|
||||
}
|
||||
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
_serviceProvider = serviceProvider;
|
||||
_editorAdaptersFactory = editorAdaptersFactory;
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
}
|
||||
|
||||
public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
|
||||
{
|
||||
if (workspaceServices == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspaceServices));
|
||||
}
|
||||
|
||||
var runningDocumentTable = (IVsRunningDocumentTable)_serviceProvider.GetService(typeof(SVsRunningDocumentTable));
|
||||
var fileChangeTrackerFactory = workspaceServices.GetRequiredService<FileChangeTrackerFactory>();
|
||||
return new VisualStudioEditorDocumentManager(_foregroundDispatcher, fileChangeTrackerFactory, runningDocumentTable, _editorAdaptersFactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.VisualStudio.OLE.Interop;
|
||||
using Microsoft.VisualStudio.TextManager.Interop;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor.Documents
|
||||
{
|
||||
internal class VsTextBufferDataEventsSink : IVsTextBufferDataEvents
|
||||
{
|
||||
private readonly Action _action;
|
||||
private readonly IConnectionPoint _connectionPoint;
|
||||
private uint _cookie;
|
||||
|
||||
public static void Subscribe(IVsTextBuffer vsTextBuffer, Action action)
|
||||
{
|
||||
if (vsTextBuffer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(vsTextBuffer));
|
||||
}
|
||||
|
||||
if (action == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(action));
|
||||
}
|
||||
|
||||
var connectionPointContainer = (IConnectionPointContainer)vsTextBuffer;
|
||||
|
||||
var guid = typeof(IVsTextBufferDataEvents).GUID;
|
||||
connectionPointContainer.FindConnectionPoint(ref guid, out var connectionPoint);
|
||||
|
||||
var sink = new VsTextBufferDataEventsSink(connectionPoint, action);
|
||||
connectionPoint.Advise(sink, out sink._cookie);
|
||||
}
|
||||
|
||||
private VsTextBufferDataEventsSink(IConnectionPoint connectionPoint, Action action)
|
||||
{
|
||||
_connectionPoint = connectionPoint;
|
||||
_action = action;
|
||||
}
|
||||
|
||||
public void OnFileChanged(uint grfChange, uint dwFileAttrs)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
public int OnLoadCompleted(int fReload)
|
||||
{
|
||||
_connectionPoint.Unadvise(_cookie);
|
||||
_action();
|
||||
|
||||
return VSConstants.S_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -40,10 +40,19 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Xaml"/>
|
||||
<Reference Include="System.Xaml" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Workaround for Microsoft.VisualStudio.SDK.EmbedInteropTypes not working correctly-->
|
||||
<Target Name="_EmbeddedAssemblyWorkaround" DependsOnTargets="ResolveReferences" BeforeTargets="FindReferenceAssembliesForReferences">
|
||||
<ItemGroup>
|
||||
<ReferencePath Condition="'%(FileName)'=='Microsoft.VisualStudio.Shell.Interop.11.0'">
|
||||
<EmbedInteropTypes>false</EmbedInteropTypes>
|
||||
</ReferencePath>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<!--
|
||||
<!--
|
||||
The ProjectSystem.SDK tasks that handle XamlPropertyRule don't work on the dotnet core version
|
||||
of MSBuild. The workaround here is to only hardcode the generated code location such that it gets
|
||||
checked in. Then we don't need to generate it at build time.
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
// 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.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Host.Mef;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.VisualStudio.Editor.Razor;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
|
|
@ -12,10 +16,36 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
{
|
||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
||||
{
|
||||
if (!IsRemoteClientWorking())
|
||||
{
|
||||
return new DefaultTagHelperResolver();
|
||||
}
|
||||
|
||||
return new OOPTagHelperResolver(
|
||||
languageServices.WorkspaceServices.GetRequiredService<ProjectSnapshotProjectEngineFactory>(),
|
||||
languageServices.WorkspaceServices.GetRequiredService<ErrorReporter>(),
|
||||
languageServices.WorkspaceServices.Workspace);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private bool IsRemoteClientWorking()
|
||||
{
|
||||
try
|
||||
{
|
||||
LoadType();
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private void LoadType()
|
||||
{
|
||||
// During 15.8 Roslyn renamed our OOP client from RazorLangaugeServiceClient to RazorLanguageServiceClient.
|
||||
GC.KeepAlive(typeof(RazorLangaugeServiceClient));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -199,7 +199,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
return;
|
||||
}
|
||||
|
||||
projectManager.DocumentAdded(_current, document);
|
||||
projectManager.DocumentAdded(_current, document, new FileTextLoader(document.FilePath, null));
|
||||
_currentDocuments.Add(document.FilePath, document);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
|
|
@ -76,10 +74,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public void ProjectSnapshot_CachesDocumentSnapshots()
|
||||
{
|
||||
// Arrange
|
||||
var state = new ProjectState(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[0])
|
||||
.WithAddedHostDocument(Documents[1])
|
||||
.WithAddedHostDocument(Documents[2]);
|
||||
var state = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[0], DocumentState.EmptyLoader)
|
||||
.WithAddedHostDocument(Documents[1], DocumentState.EmptyLoader)
|
||||
.WithAddedHostDocument(Documents[2], DocumentState.EmptyLoader);
|
||||
var snapshot = new DefaultProjectSnapshot(state);
|
||||
|
||||
// Act
|
||||
|
|
@ -101,7 +99,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
try
|
||||
{
|
||||
var state = new ProjectState(Workspace.Services, HostProject, WorkspaceProject);
|
||||
var state = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject);
|
||||
var snapshot = new DefaultProjectSnapshot(state);
|
||||
|
||||
// Act
|
||||
|
|
|
|||
|
|
@ -5,8 +5,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;
|
||||
|
||||
|
|
@ -47,6 +49,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
SomeTagHelpers.Add(TagHelperDescriptorBuilder.Create("Test1", "TestAssembly").Build());
|
||||
|
||||
Document = new HostDocument("c:\\MyProject\\File.cshtml", "File.cshtml");
|
||||
|
||||
Text = SourceText.From("Hello, world!");
|
||||
TextLoader = () => Task.FromResult(TextAndVersion.Create(Text, VersionStamp.Create()));
|
||||
}
|
||||
|
||||
private HostDocument Document { get; }
|
||||
|
|
@ -65,29 +70,127 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
private List<TagHelperDescriptor> SomeTagHelpers { get; }
|
||||
|
||||
private Func<Task<TextAndVersion>> TextLoader { get; }
|
||||
|
||||
private SourceText Text { get; }
|
||||
|
||||
[Fact]
|
||||
public void DocumentState_ConstructedNew()
|
||||
public async Task DocumentState_CreatedNew_HasEmptyText()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
var state = new DocumentState(Workspace.Services, Document);
|
||||
|
||||
// Arrange & Act
|
||||
var state = DocumentState.Create(Workspace.Services, Document, DocumentState.EmptyLoader);
|
||||
|
||||
// Assert
|
||||
Assert.NotEqual(VersionStamp.Default, state.Version);
|
||||
var text = await state.GetTextAsync();
|
||||
Assert.Equal(0, text.Length);
|
||||
}
|
||||
|
||||
[Fact] // There's no magic in the constructor.
|
||||
public void ProjectState_ConstructedFromCopy()
|
||||
[Fact]
|
||||
public async Task DocumentState_WithText_CreatesNewState()
|
||||
{
|
||||
// Arrange
|
||||
var original = new DocumentState(Workspace.Services, Document);
|
||||
var original = DocumentState.Create(Workspace.Services, Document, DocumentState.EmptyLoader);
|
||||
|
||||
// Act
|
||||
var state = new DocumentState(original, ProjectDifference.ConfigurationChanged);
|
||||
var state = original.WithText(Text, VersionStamp.Create());
|
||||
|
||||
// Assert
|
||||
Assert.NotEqual(original.Version, state.Version);
|
||||
var text = await state.GetTextAsync();
|
||||
Assert.Same(Text, text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DocumentState_WithTextLoader_CreatesNewState()
|
||||
{
|
||||
// Arrange
|
||||
var original = DocumentState.Create(Workspace.Services, Document, DocumentState.EmptyLoader);
|
||||
|
||||
// Act
|
||||
var state = original.WithTextLoader(TextLoader);
|
||||
|
||||
// Assert
|
||||
var text = await state.GetTextAsync();
|
||||
Assert.Same(Text, text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DocumentState_WithConfigurationChange_CachesSnapshotText()
|
||||
{
|
||||
// Arrange
|
||||
var original = DocumentState.Create(Workspace.Services, Document, DocumentState.EmptyLoader)
|
||||
.WithText(Text, VersionStamp.Create());
|
||||
|
||||
// Act
|
||||
var state = original.WithConfigurationChange();
|
||||
|
||||
// Assert
|
||||
Assert.True(state.TryGetText(out _));
|
||||
Assert.True(state.TryGetTextVersion(out _));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DocumentState_WithConfigurationChange_CachesLoadedText()
|
||||
{
|
||||
// Arrange
|
||||
var original = DocumentState.Create(Workspace.Services, Document, DocumentState.EmptyLoader)
|
||||
.WithTextLoader(TextLoader);
|
||||
|
||||
await original.GetTextAsync();
|
||||
|
||||
// Act
|
||||
var state = original.WithConfigurationChange();
|
||||
|
||||
// Assert
|
||||
Assert.True(state.TryGetText(out _));
|
||||
Assert.True(state.TryGetTextVersion(out _));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DocumentState_WithWorkspaceProjectChange_CachesSnapshotText()
|
||||
{
|
||||
// Arrange
|
||||
var original = DocumentState.Create(Workspace.Services, Document, DocumentState.EmptyLoader)
|
||||
.WithText(Text, VersionStamp.Create());
|
||||
|
||||
// Act
|
||||
var state = original.WithWorkspaceProjectChange();
|
||||
|
||||
// Assert
|
||||
Assert.True(state.TryGetText(out _));
|
||||
Assert.True(state.TryGetTextVersion(out _));
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task DocumentState_WithWorkspaceProjectChange_CachesLoadedText()
|
||||
{
|
||||
// Arrange
|
||||
var original = DocumentState.Create(Workspace.Services, Document, DocumentState.EmptyLoader)
|
||||
.WithTextLoader(TextLoader);
|
||||
|
||||
await original.GetTextAsync();
|
||||
|
||||
// Act
|
||||
var state = original.WithWorkspaceProjectChange();
|
||||
|
||||
// Assert
|
||||
Assert.True(state.TryGetText(out _));
|
||||
Assert.True(state.TryGetTextVersion(out _));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DocumentState_WithWorkspaceProjectChange_TriesToCacheGeneratedOutput()
|
||||
{
|
||||
// Arrange
|
||||
var original = DocumentState.Create(Workspace.Services, Document, DocumentState.EmptyLoader);
|
||||
|
||||
GC.KeepAlive(original.GeneratedOutput);
|
||||
|
||||
// Act
|
||||
var state = original.WithWorkspaceProjectChange();
|
||||
|
||||
// Assert
|
||||
Assert.Same(state.GeneratedOutput.Older, original.GeneratedOutput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,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;
|
||||
|
||||
|
|
@ -54,6 +56,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// linked file
|
||||
new HostDocument("c:\\SomeOtherProject\\Index.cshtml", "Pages\\Index.cshtml"),
|
||||
};
|
||||
|
||||
Text = SourceText.From("Hello, world!");
|
||||
TextLoader = () => Task.FromResult(TextAndVersion.Create(Text, VersionStamp.Create()));
|
||||
}
|
||||
|
||||
private HostDocument[] Documents { get; }
|
||||
|
|
@ -72,41 +77,31 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
private List<TagHelperDescriptor> SomeTagHelpers { get; }
|
||||
|
||||
private Func<Task<TextAndVersion>> TextLoader { get; }
|
||||
|
||||
private SourceText Text { get; }
|
||||
|
||||
[Fact]
|
||||
public void ProjectState_ConstructedNew()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
var state = new ProjectState(Workspace.Services, HostProject, WorkspaceProject);
|
||||
var state = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(state.Documents);
|
||||
Assert.NotEqual(VersionStamp.Default, state.Version);
|
||||
}
|
||||
|
||||
[Fact] // There's no magic in the constructor.
|
||||
public void ProjectState_ConstructedFromCopy()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProjectState(Workspace.Services, HostProject, WorkspaceProject);
|
||||
|
||||
// Act
|
||||
var state = new ProjectState(original, ProjectDifference.None, HostProject, WorkspaceProject, original.Documents);
|
||||
|
||||
// Assert
|
||||
Assert.Same(original.Documents, state.Documents);
|
||||
Assert.NotEqual(original.Version, state.Version);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectState_AddHostDocument_ToEmpty()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProjectState(Workspace.Services, HostProject, WorkspaceProject);
|
||||
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject);
|
||||
|
||||
// Act
|
||||
var state = original.WithAddedHostDocument(Documents[0]);
|
||||
var state = original.WithAddedHostDocument(Documents[0], DocumentState.EmptyLoader);
|
||||
|
||||
// Assert
|
||||
Assert.NotEqual(original.Version, state.Version);
|
||||
|
|
@ -116,16 +111,30 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
d => Assert.Same(Documents[0], d.Value.HostDocument));
|
||||
}
|
||||
|
||||
[Fact] // When we first add a document, we have no way to read the text, so it's empty.
|
||||
public async Task ProjectState_AddHostDocument_DocumentIsEmpty()
|
||||
{
|
||||
// Arrange
|
||||
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject);
|
||||
|
||||
// Act
|
||||
var state = original.WithAddedHostDocument(Documents[0], DocumentState.EmptyLoader);
|
||||
|
||||
// Assert
|
||||
var text = await state.Documents[Documents[0].FilePath].GetTextAsync();
|
||||
Assert.Equal(0, text.Length);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectState_AddHostDocument_ToProjectWithDocuments()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProjectState(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[2])
|
||||
.WithAddedHostDocument(Documents[1]);
|
||||
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[2], DocumentState.EmptyLoader)
|
||||
.WithAddedHostDocument(Documents[1], DocumentState.EmptyLoader);
|
||||
|
||||
// Act
|
||||
var state = original.WithAddedHostDocument(Documents[0]);
|
||||
var state = original.WithAddedHostDocument(Documents[0], DocumentState.EmptyLoader);
|
||||
|
||||
// Assert
|
||||
Assert.NotEqual(original.Version, state.Version);
|
||||
|
|
@ -141,16 +150,16 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public void ProjectState_AddHostDocument_RetainsComputedState()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProjectState(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[2])
|
||||
.WithAddedHostDocument(Documents[1]);
|
||||
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[2], DocumentState.EmptyLoader)
|
||||
.WithAddedHostDocument(Documents[1], DocumentState.EmptyLoader);
|
||||
|
||||
// Force init
|
||||
GC.KeepAlive(original.ProjectEngine);
|
||||
GC.KeepAlive(original.TagHelpers);
|
||||
|
||||
// Act
|
||||
var state = original.WithAddedHostDocument(Documents[0]);
|
||||
var state = original.WithAddedHostDocument(Documents[0], DocumentState.EmptyLoader);
|
||||
|
||||
// Assert
|
||||
Assert.Same(original.ProjectEngine, state.ProjectEngine);
|
||||
|
|
@ -164,12 +173,122 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public void ProjectState_AddHostDocument_DuplicateNoops()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProjectState(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[2])
|
||||
.WithAddedHostDocument(Documents[1]);
|
||||
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[2], DocumentState.EmptyLoader)
|
||||
.WithAddedHostDocument(Documents[1], DocumentState.EmptyLoader);
|
||||
|
||||
// Act
|
||||
var state = original.WithAddedHostDocument(new HostDocument(Documents[1].FilePath, "SomePath.cshtml"));
|
||||
var state = original.WithAddedHostDocument(new HostDocument(Documents[1].FilePath, "SomePath.cshtml"), DocumentState.EmptyLoader);
|
||||
|
||||
// Assert
|
||||
Assert.Same(original, state);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProjectState_WithChangedHostDocument_Loader()
|
||||
{
|
||||
// Arrange
|
||||
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[2], DocumentState.EmptyLoader)
|
||||
.WithAddedHostDocument(Documents[1], DocumentState.EmptyLoader);
|
||||
|
||||
// Act
|
||||
var state = original.WithChangedHostDocument(Documents[1], TextLoader);
|
||||
|
||||
// Assert
|
||||
Assert.NotEqual(original.Version, state.Version);
|
||||
|
||||
var text = await state.Documents[Documents[1].FilePath].GetTextAsync();
|
||||
Assert.Same(Text, text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProjectState_WithChangedHostDocument_Snapshot()
|
||||
{
|
||||
// Arrange
|
||||
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[2], DocumentState.EmptyLoader)
|
||||
.WithAddedHostDocument(Documents[1], DocumentState.EmptyLoader);
|
||||
|
||||
// Act
|
||||
var state = original.WithChangedHostDocument(Documents[1], Text, VersionStamp.Create());
|
||||
|
||||
// Assert
|
||||
Assert.NotEqual(original.Version, state.Version);
|
||||
|
||||
var text = await state.Documents[Documents[1].FilePath].GetTextAsync();
|
||||
Assert.Same(Text, text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectState_WithChangedHostDocument_Loader_RetainsComputedState()
|
||||
{
|
||||
// Arrange
|
||||
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[2], DocumentState.EmptyLoader)
|
||||
.WithAddedHostDocument(Documents[1], DocumentState.EmptyLoader);
|
||||
|
||||
// Force init
|
||||
GC.KeepAlive(original.ProjectEngine);
|
||||
GC.KeepAlive(original.TagHelpers);
|
||||
|
||||
// Act
|
||||
var state = original.WithChangedHostDocument(Documents[1], TextLoader);
|
||||
|
||||
// Assert
|
||||
Assert.Same(original.ProjectEngine, state.ProjectEngine);
|
||||
Assert.Same(original.TagHelpers, state.TagHelpers);
|
||||
|
||||
Assert.NotSame(original.Documents[Documents[1].FilePath], state.Documents[Documents[1].FilePath]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectState_WithChangedHostDocument_Snapshot_RetainsComputedState()
|
||||
{
|
||||
// Arrange
|
||||
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[2], DocumentState.EmptyLoader)
|
||||
.WithAddedHostDocument(Documents[1], DocumentState.EmptyLoader);
|
||||
|
||||
// Force init
|
||||
GC.KeepAlive(original.ProjectEngine);
|
||||
GC.KeepAlive(original.TagHelpers);
|
||||
|
||||
// Act
|
||||
var state = original.WithChangedHostDocument(Documents[1], Text, VersionStamp.Create());
|
||||
|
||||
// Assert
|
||||
Assert.Same(original.ProjectEngine, state.ProjectEngine);
|
||||
Assert.Same(original.TagHelpers, state.TagHelpers);
|
||||
|
||||
Assert.NotSame(original.Documents[Documents[1].FilePath], state.Documents[Documents[1].FilePath]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectState_WithChangedHostDocument_Loader_NotFoundNoops()
|
||||
{
|
||||
// Arrange
|
||||
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[2], DocumentState.EmptyLoader)
|
||||
.WithAddedHostDocument(Documents[1], DocumentState.EmptyLoader);
|
||||
|
||||
// Act
|
||||
var state = original.WithChangedHostDocument(Documents[0], TextLoader);
|
||||
|
||||
// Assert
|
||||
Assert.Same(original, state);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectState_WithChangedHostDocument_Snapshot_NotFoundNoops()
|
||||
{
|
||||
// Arrange
|
||||
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[2], DocumentState.EmptyLoader)
|
||||
.WithAddedHostDocument(Documents[1], DocumentState.EmptyLoader);
|
||||
|
||||
// Act
|
||||
var state = original.WithChangedHostDocument(Documents[0], Text, VersionStamp.Create());
|
||||
|
||||
// Assert
|
||||
Assert.Same(original, state);
|
||||
|
|
@ -179,9 +298,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public void ProjectState_RemoveHostDocument_FromProjectWithDocuments()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProjectState(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[2])
|
||||
.WithAddedHostDocument(Documents[1]);
|
||||
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[2], DocumentState.EmptyLoader)
|
||||
.WithAddedHostDocument(Documents[1], DocumentState.EmptyLoader);
|
||||
|
||||
// Act
|
||||
var state = original.WithRemovedHostDocument(Documents[1]);
|
||||
|
|
@ -198,9 +317,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public void ProjectState_RemoveHostDocument_RetainsComputedState()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProjectState(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[2])
|
||||
.WithAddedHostDocument(Documents[1]);
|
||||
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[2], DocumentState.EmptyLoader)
|
||||
.WithAddedHostDocument(Documents[1], DocumentState.EmptyLoader);
|
||||
|
||||
// Force init
|
||||
GC.KeepAlive(original.ProjectEngine);
|
||||
|
|
@ -220,9 +339,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public void ProjectState_RemoveHostDocument_NotFoundNoops()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProjectState(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[2])
|
||||
.WithAddedHostDocument(Documents[1]);
|
||||
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[2], DocumentState.EmptyLoader)
|
||||
.WithAddedHostDocument(Documents[1], DocumentState.EmptyLoader);
|
||||
|
||||
// Act
|
||||
var state = original.WithRemovedHostDocument(Documents[0]);
|
||||
|
|
@ -235,9 +354,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public void ProjectState_WithHostProject_ConfigurationChange_UpdatesComputedState()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProjectState(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[2])
|
||||
.WithAddedHostDocument(Documents[1]);
|
||||
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[2], DocumentState.EmptyLoader)
|
||||
.WithAddedHostDocument(Documents[1], DocumentState.EmptyLoader);
|
||||
|
||||
// Force init
|
||||
GC.KeepAlive(original.ProjectEngine);
|
||||
|
|
@ -261,9 +380,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public void ProjectState_WithHostProject_NoConfigurationChange_Noops()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProjectState(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[2])
|
||||
.WithAddedHostDocument(Documents[1]);
|
||||
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[2], DocumentState.EmptyLoader)
|
||||
.WithAddedHostDocument(Documents[1], DocumentState.EmptyLoader);
|
||||
|
||||
// Force init
|
||||
GC.KeepAlive(original.ProjectEngine);
|
||||
|
|
@ -280,9 +399,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public void ProjectState_WithWorkspaceProject_Removed()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProjectState(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[2])
|
||||
.WithAddedHostDocument(Documents[1]);
|
||||
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[2], DocumentState.EmptyLoader)
|
||||
.WithAddedHostDocument(Documents[1], DocumentState.EmptyLoader);
|
||||
|
||||
// Force init
|
||||
GC.KeepAlive(original.ProjectEngine);
|
||||
|
|
@ -306,9 +425,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public void ProjectState_WithWorkspaceProject_Added()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProjectState(Workspace.Services, HostProject, null)
|
||||
.WithAddedHostDocument(Documents[2])
|
||||
.WithAddedHostDocument(Documents[1]);
|
||||
var original = ProjectState.Create(Workspace.Services, HostProject, null)
|
||||
.WithAddedHostDocument(Documents[2], DocumentState.EmptyLoader)
|
||||
.WithAddedHostDocument(Documents[1], DocumentState.EmptyLoader);
|
||||
|
||||
// Force init
|
||||
GC.KeepAlive(original.ProjectEngine);
|
||||
|
|
@ -332,9 +451,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public void ProjectState_WithWorkspaceProject_Changed()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProjectState(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[2])
|
||||
.WithAddedHostDocument(Documents[1]);
|
||||
var original = ProjectState.Create(Workspace.Services, HostProject, WorkspaceProject)
|
||||
.WithAddedHostDocument(Documents[2], DocumentState.EmptyLoader)
|
||||
.WithAddedHostDocument(Documents[1], DocumentState.EmptyLoader);
|
||||
|
||||
// Force init
|
||||
GC.KeepAlive(original.ProjectEngine);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.IO;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace Microsoft.VisualStudio.Text
|
||||
{
|
||||
public class StringTextImage : ITextImage
|
||||
{
|
||||
private readonly SourceText _sourceText;
|
||||
private readonly string _text;
|
||||
|
||||
public StringTextImage(string text)
|
||||
{
|
||||
_text = text;
|
||||
_sourceText = SourceText.From(text);
|
||||
}
|
||||
|
||||
public char this[int position] => _text[position];
|
||||
|
||||
public ITextImageVersion Version => null;
|
||||
|
||||
public int Length => _text.Length;
|
||||
|
||||
public int LineCount => _sourceText.Lines.Count;
|
||||
|
||||
public void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count)
|
||||
{
|
||||
_text.CopyTo(sourceIndex, destination, destinationIndex, count);
|
||||
}
|
||||
|
||||
public TextImageLine GetLineFromLineNumber(int lineNumber)
|
||||
{
|
||||
var line = _sourceText.Lines[lineNumber];
|
||||
return new TextImageLine(this, lineNumber, new Span(line.Start, line.End - line.Start), line.EndIncludingLineBreak - line.End);
|
||||
}
|
||||
|
||||
public TextImageLine GetLineFromPosition(int position)
|
||||
{
|
||||
var line = _sourceText.Lines.GetLineFromPosition(position);
|
||||
return new TextImageLine(this, line.LineNumber, new Span(line.Start, line.End - line.Start), line.EndIncludingLineBreak - line.End);
|
||||
}
|
||||
|
||||
public int GetLineNumberFromPosition(int position)
|
||||
{
|
||||
return _sourceText.Lines.GetLineFromPosition(position).LineNumber;
|
||||
}
|
||||
|
||||
public ITextImage GetSubText(Span span)
|
||||
{
|
||||
return new StringTextImage(_text.Substring(span.Start, span.Length));
|
||||
}
|
||||
|
||||
public string GetText(Span span)
|
||||
{
|
||||
return _text.Substring(span.Start, span.Length);
|
||||
}
|
||||
|
||||
public char[] ToCharArray(int startIndex, int length)
|
||||
{
|
||||
return _text.ToCharArray(startIndex, length);
|
||||
}
|
||||
|
||||
public void Write(TextWriter writer, Span span)
|
||||
{
|
||||
writer.Write(_text.Substring(span.Start, span.Length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,12 +5,13 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
using Microsoft.VisualStudio.Utilities;
|
||||
|
||||
namespace Microsoft.VisualStudio.Text
|
||||
{
|
||||
public class StringTextSnapshot : ITextSnapshot
|
||||
public class StringTextSnapshot : ITextSnapshot2
|
||||
{
|
||||
private readonly List<ITextSnapshotLine> _lines;
|
||||
|
||||
|
|
@ -49,7 +50,7 @@ namespace Microsoft.VisualStudio.Text
|
|||
|
||||
public int Length => Content.Length;
|
||||
|
||||
public ITextBuffer TextBuffer => throw new NotImplementedException();
|
||||
public ITextBuffer TextBuffer { get; set; }
|
||||
|
||||
public IContentType ContentType => throw new NotImplementedException();
|
||||
|
||||
|
|
@ -57,6 +58,8 @@ namespace Microsoft.VisualStudio.Text
|
|||
|
||||
public IEnumerable<ITextSnapshotLine> Lines => throw new NotImplementedException();
|
||||
|
||||
public ITextImage TextImage => new StringTextImage(Content);
|
||||
|
||||
public void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count) => Content.CopyTo(sourceIndex, destination, destinationIndex, count);
|
||||
|
||||
public string GetText(int startIndex, int length) => Content.Substring(startIndex, length);
|
||||
|
|
@ -110,6 +113,11 @@ namespace Microsoft.VisualStudio.Text
|
|||
|
||||
public void Write(TextWriter writer) => throw new NotImplementedException();
|
||||
|
||||
public void SaveToFile(string filePath, bool replaceFile, Encoding encoding)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private class TextVersion : ITextVersion
|
||||
{
|
||||
public INormalizedTextChangeCollection Changes { get; } = new TextChangeCollection();
|
||||
|
|
|
|||
|
|
@ -40,11 +40,11 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
"/TestPath/SomePath/Test.csproj",
|
||||
new ProjectSystemRazorConfiguration(RazorLanguageVersion.Version_2_1, "Blazor-0.1", Array.Empty<RazorExtension>()));
|
||||
|
||||
Snapshot_For_1_0 = new DefaultProjectSnapshot(new ProjectState(Workspace.Services, HostProject_For_1_0, WorkspaceProject));
|
||||
Snapshot_For_1_1 = new DefaultProjectSnapshot(new ProjectState(Workspace.Services, HostProject_For_1_1, WorkspaceProject));
|
||||
Snapshot_For_2_0 = new DefaultProjectSnapshot(new ProjectState(Workspace.Services, HostProject_For_2_0, WorkspaceProject));
|
||||
Snapshot_For_2_1 = new DefaultProjectSnapshot(new ProjectState(Workspace.Services, HostProject_For_2_1, WorkspaceProject));
|
||||
Snapshot_For_UnknownConfiguration = new DefaultProjectSnapshot(new ProjectState(Workspace.Services, HostProject_For_UnknownConfiguration, WorkspaceProject));
|
||||
Snapshot_For_1_0 = new DefaultProjectSnapshot(ProjectState.Create(Workspace.Services, HostProject_For_1_0, WorkspaceProject));
|
||||
Snapshot_For_1_1 = new DefaultProjectSnapshot(ProjectState.Create(Workspace.Services, HostProject_For_1_1, WorkspaceProject));
|
||||
Snapshot_For_2_0 = new DefaultProjectSnapshot(ProjectState.Create(Workspace.Services, HostProject_For_2_0, WorkspaceProject));
|
||||
Snapshot_For_2_1 = new DefaultProjectSnapshot(ProjectState.Create(Workspace.Services, HostProject_For_2_1, WorkspaceProject));
|
||||
Snapshot_For_UnknownConfiguration = new DefaultProjectSnapshot(ProjectState.Create(Workspace.Services, HostProject_For_UnknownConfiguration, WorkspaceProject));
|
||||
|
||||
CustomFactories = new Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[]
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,212 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.VisualStudio.Test;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor.Documents
|
||||
{
|
||||
public class EditorDocumentManagerBaseTest : ForegroundDispatcherTestBase
|
||||
{
|
||||
public EditorDocumentManagerBaseTest()
|
||||
{
|
||||
|
||||
Manager = new TestEditorDocumentManager(Dispatcher);
|
||||
}
|
||||
|
||||
private TestEditorDocumentManager Manager { get; }
|
||||
|
||||
public string Project1 => "c:\\Project1";
|
||||
|
||||
public string Project2 => "c:\\Project2";
|
||||
|
||||
public string File1 => "c:\\Project1\\File1.cshtml";
|
||||
|
||||
public string File2 => "c:\\Project2\\File2.cshtml";
|
||||
|
||||
public TestTextBuffer TextBuffer => new TestTextBuffer(new StringTextSnapshot("HI"));
|
||||
|
||||
[ForegroundFact]
|
||||
public void GetOrCreateDocument_CreatesAndCachesDocument()
|
||||
{
|
||||
// Arrange
|
||||
var expected = Manager.GetOrCreateDocument(new DocumentKey(Project1, File1), null, null, null, null);
|
||||
|
||||
// Act
|
||||
Manager.TryGetDocument(new DocumentKey(Project1, File1), out var actual);
|
||||
|
||||
// Assert
|
||||
Assert.Same(expected, actual);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void GetOrCreateDocument_NoOp()
|
||||
{
|
||||
// Arrange
|
||||
var expected = Manager.GetOrCreateDocument(new DocumentKey(Project1, File1), null, null, null, null);
|
||||
|
||||
// Act
|
||||
var actual = Manager.GetOrCreateDocument(new DocumentKey(Project1, File1), null, null, null, null);
|
||||
|
||||
// Assert
|
||||
Assert.Same(expected, actual);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void GetOrCreateDocument_SameFile_MulipleProjects()
|
||||
{
|
||||
// Arrange
|
||||
var document1 = Manager.GetOrCreateDocument(new DocumentKey(Project1, File1), null, null, null, null);
|
||||
|
||||
// Act
|
||||
var document2 = Manager.GetOrCreateDocument(new DocumentKey(Project2, File1), null, null, null, null);
|
||||
|
||||
// Assert
|
||||
Assert.NotSame(document1, document2);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void GetOrCreateDocument_MulipleFiles_SameProject()
|
||||
{
|
||||
// Arrange
|
||||
var document1 = Manager.GetOrCreateDocument(new DocumentKey(Project1, File1), null, null, null, null);
|
||||
|
||||
// Act
|
||||
var document2 = Manager.GetOrCreateDocument(new DocumentKey(Project1, File2), null, null, null, null);
|
||||
|
||||
// Assert
|
||||
Assert.NotSame(document1, document2);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void GetOrCreateDocument_WithBuffer_AttachesBuffer()
|
||||
{
|
||||
// Arrange
|
||||
Manager.Buffers.Add(File1, TextBuffer);
|
||||
|
||||
// Act
|
||||
var document = Manager.GetOrCreateDocument(new DocumentKey(Project1, File1), null, null, null, null);
|
||||
|
||||
// Assert
|
||||
Assert.True(document.IsOpenInEditor);
|
||||
Assert.NotNull(document.EditorTextBuffer);
|
||||
|
||||
Assert.Same(document, Assert.Single(Manager.Opened));
|
||||
Assert.Empty(Manager.Closed);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void TryGetMatchingDocuments_MultipleDocuments()
|
||||
{
|
||||
// Arrange
|
||||
var document1 = Manager.GetOrCreateDocument(new DocumentKey(Project1, File1), null, null, null, null);
|
||||
var document2 = Manager.GetOrCreateDocument(new DocumentKey(Project2, File1), null, null, null, null);
|
||||
|
||||
// Act
|
||||
Manager.TryGetMatchingDocuments(File1, out var documents);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
documents.OrderBy(d => d.ProjectFilePath),
|
||||
d => Assert.Same(document1, d),
|
||||
d => Assert.Same(document2, d));
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void RemoveDocument_MultipleDocuments_RemovesOne()
|
||||
{
|
||||
// Arrange
|
||||
var document1 = Manager.GetOrCreateDocument(new DocumentKey(Project1, File1), null, null, null, null);
|
||||
var document2 = Manager.GetOrCreateDocument(new DocumentKey(Project2, File1), null, null, null, null);
|
||||
|
||||
// Act
|
||||
Manager.RemoveDocument(document1);
|
||||
|
||||
// Assert
|
||||
Manager.TryGetMatchingDocuments(File1, out var documents);
|
||||
Assert.Collection(
|
||||
documents.OrderBy(d => d.ProjectFilePath),
|
||||
d => Assert.Same(document2, d));
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void DocumentOpened_MultipleDocuments_OpensAll()
|
||||
{
|
||||
// Arrange
|
||||
var document1 = Manager.GetOrCreateDocument(new DocumentKey(Project1, File1), null, null, null, null);
|
||||
var document2 = Manager.GetOrCreateDocument(new DocumentKey(Project2, File1), null, null, null, null);
|
||||
|
||||
// Act
|
||||
Manager.DocumentOpened(File1, TextBuffer);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
Manager.Opened.OrderBy(d => d.ProjectFilePath),
|
||||
d => Assert.Same(document1, d),
|
||||
d => Assert.Same(document2, d));
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void DocumentOpened_MultipleDocuments_ClosesAll()
|
||||
{
|
||||
// Arrange
|
||||
var document1 = Manager.GetOrCreateDocument(new DocumentKey(Project1, File1), null, null, null, null);
|
||||
var document2 = Manager.GetOrCreateDocument(new DocumentKey(Project2, File1), null, null, null, null);
|
||||
Manager.DocumentOpened(File1, TextBuffer);
|
||||
|
||||
// Act
|
||||
Manager.DocumentClosed(File1);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
Manager.Closed.OrderBy(d => d.ProjectFilePath),
|
||||
d => Assert.Same(document1, d),
|
||||
d => Assert.Same(document2, d));
|
||||
}
|
||||
|
||||
private class TestEditorDocumentManager : EditorDocumentManagerBase
|
||||
{
|
||||
public TestEditorDocumentManager(ForegroundDispatcher foregroundDispatcher)
|
||||
: base(foregroundDispatcher, new DefaultFileChangeTrackerFactory())
|
||||
{
|
||||
}
|
||||
|
||||
public List<EditorDocument> Opened { get; } = new List<EditorDocument>();
|
||||
|
||||
public List<EditorDocument> Closed { get; } = new List<EditorDocument>();
|
||||
|
||||
public Dictionary<string, ITextBuffer> Buffers { get; } = new Dictionary<string, ITextBuffer>();
|
||||
|
||||
public new void DocumentOpened(string filePath, ITextBuffer textBuffer)
|
||||
{
|
||||
base.DocumentOpened(filePath, textBuffer);
|
||||
}
|
||||
|
||||
public new void DocumentClosed(string filePath)
|
||||
{
|
||||
base.DocumentClosed(filePath);
|
||||
}
|
||||
|
||||
protected override ITextBuffer GetTextBufferForOpenDocument(string filePath)
|
||||
{
|
||||
Buffers.TryGetValue(filePath, out var buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
protected override void OnDocumentOpened(EditorDocument document)
|
||||
{
|
||||
Opened.Add(document);
|
||||
}
|
||||
|
||||
protected override void OnDocumentClosed(EditorDocument document)
|
||||
{
|
||||
Closed.Add(document);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
// 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.Text;
|
||||
using Microsoft.VisualStudio.Test;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor.Documents
|
||||
{
|
||||
public class EditorDocumentTest
|
||||
{
|
||||
public EditorDocumentTest()
|
||||
{
|
||||
DocumentManager = Mock.Of<EditorDocumentManager>();
|
||||
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 EditorDocumentManager DocumentManager { get; }
|
||||
|
||||
private string ProjectFilePath { get; }
|
||||
|
||||
private string DocumentFilePath { get; }
|
||||
|
||||
private TextLoader TextLoader { get; }
|
||||
|
||||
private FileChangeTracker FileChangeTracker { get; }
|
||||
|
||||
private TestTextBuffer TextBuffer { get; }
|
||||
|
||||
[Fact]
|
||||
public void EditorDocument_CreatedWhileOpened()
|
||||
{
|
||||
// Arrange & Act
|
||||
var document = new EditorDocument(
|
||||
DocumentManager,
|
||||
ProjectFilePath,
|
||||
DocumentFilePath,
|
||||
TextLoader,
|
||||
FileChangeTracker,
|
||||
TextBuffer,
|
||||
changedOnDisk: null,
|
||||
changedInEditor: null,
|
||||
opened: null,
|
||||
closed: null);
|
||||
|
||||
// Assert
|
||||
Assert.True(document.IsOpenInEditor);
|
||||
Assert.Same(TextBuffer, document.EditorTextBuffer);
|
||||
Assert.NotNull(document.EditorTextContainer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EditorDocument_CreatedWhileClosed()
|
||||
{
|
||||
// Arrange & Act
|
||||
var document = new EditorDocument(
|
||||
DocumentManager,
|
||||
ProjectFilePath,
|
||||
DocumentFilePath,
|
||||
TextLoader,
|
||||
FileChangeTracker,
|
||||
null,
|
||||
changedOnDisk: null,
|
||||
changedInEditor: null,
|
||||
opened: null,
|
||||
closed: null);
|
||||
|
||||
// Assert
|
||||
Assert.False(document.IsOpenInEditor);
|
||||
Assert.Null(document.EditorTextBuffer);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,11 @@ namespace Microsoft.VisualStudio.Test
|
|||
public TestTextBuffer(ITextSnapshot initialSnapshot)
|
||||
{
|
||||
_currentSnapshot = initialSnapshot;
|
||||
if (_currentSnapshot is StringTextSnapshot testSnapshot)
|
||||
{
|
||||
testSnapshot.TextBuffer = this;
|
||||
}
|
||||
|
||||
_attachedChangedEvents = new List<EventHandler<TextContentChangedEventArgs>>();
|
||||
|
||||
ReadOnlyRegionsChanged += (sender, args) => { };
|
||||
|
|
@ -41,6 +46,10 @@ namespace Microsoft.VisualStudio.Test
|
|||
}
|
||||
|
||||
_currentSnapshot = edits[edits.Length - 1].NewSnapshot;
|
||||
if (_currentSnapshot is StringTextSnapshot testSnapshot)
|
||||
{
|
||||
testSnapshot.TextBuffer = this;
|
||||
}
|
||||
|
||||
foreach (var changedEvent in AttachedChangedEvents)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -71,8 +71,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
projectManager.HostProjectAdded(HostProject2);
|
||||
projectManager.WorkspaceProjectAdded(WorkspaceProject1);
|
||||
projectManager.WorkspaceProjectAdded(WorkspaceProject2);
|
||||
projectManager.DocumentAdded(HostProject1, Documents[0]);
|
||||
projectManager.DocumentAdded(HostProject1, Documents[1]);
|
||||
projectManager.DocumentAdded(HostProject1, Documents[0], null);
|
||||
projectManager.DocumentAdded(HostProject1, Documents[1], null);
|
||||
|
||||
var project = projectManager.GetLoadedProject(HostProject1.FilePath);
|
||||
|
||||
|
|
@ -110,8 +110,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
projectManager.HostProjectAdded(HostProject2);
|
||||
projectManager.WorkspaceProjectAdded(WorkspaceProject1);
|
||||
projectManager.WorkspaceProjectAdded(WorkspaceProject2);
|
||||
projectManager.DocumentAdded(HostProject1, Documents[0]);
|
||||
projectManager.DocumentAdded(HostProject1, Documents[1]);
|
||||
projectManager.DocumentAdded(HostProject1, Documents[0], null);
|
||||
projectManager.DocumentAdded(HostProject1, Documents[1], null);
|
||||
|
||||
var project = projectManager.GetLoadedProject(HostProject1.FilePath);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using System.Linq;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -85,6 +86,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
SomeTagHelpers = TagHelperResolver.TagHelpers;
|
||||
SomeTagHelpers.Add(TagHelperDescriptorBuilder.Create("Test1", "TestAssembly").Build());
|
||||
|
||||
SourceText = SourceText.From("Hello world");
|
||||
}
|
||||
|
||||
private HostDocument[] Documents { get; }
|
||||
|
|
@ -109,6 +112,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
private Workspace Workspace { get; }
|
||||
|
||||
private SourceText SourceText { get; }
|
||||
|
||||
private IList<TagHelperDescriptor> SomeTagHelpers { get; }
|
||||
|
||||
[ForegroundFact]
|
||||
|
|
@ -120,13 +125,13 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0]);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0], null);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
Assert.Collection(snapshot.DocumentFilePaths, d => Assert.Equal(Documents[0].FilePath, d));
|
||||
|
||||
Assert.Equal(ProjectChangeKind.DocumentsChanged, ProjectManager.ListenersNotifiedOf);
|
||||
Assert.Equal(ProjectChangeKind.DocumentAdded, ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
|
|
@ -135,11 +140,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0]);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0], null);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0]);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0], null);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
|
|
@ -154,13 +159,53 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// Arrange
|
||||
|
||||
// Act
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0]);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0], null);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
Assert.Null(snapshot);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task DocumentAdded_NullLoader_HasEmptyText()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0], null);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
var document = snapshot.GetDocument(snapshot.DocumentFilePaths.Single());
|
||||
|
||||
var text = await document.GetTextAsync();
|
||||
Assert.Equal(0, text.Length);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task DocumentAdded_WithLoader_LoadesText()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var expected = SourceText.From("Hello");
|
||||
|
||||
// Act
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0], TextLoader.From(TextAndVersion.Create(expected,VersionStamp.Default)));
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
var document = snapshot.GetDocument(snapshot.DocumentFilePaths.Single());
|
||||
|
||||
var actual = await document.GetTextAsync();
|
||||
Assert.Same(expected, actual);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task DocumentAdded_CachesTagHelpers()
|
||||
{
|
||||
|
|
@ -175,7 +220,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
await snapshot.GetTagHelpersAsync();
|
||||
|
||||
// Act
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0]);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0], null);
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
|
|
@ -193,7 +238,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
var projectEngine = snapshot.GetProjectEngine();
|
||||
|
||||
// Act
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0]);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0], null);
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
|
|
@ -206,9 +251,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0]);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[1]);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[2]);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0], null);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[1], null);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[2], null);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
|
|
@ -221,7 +266,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
d => Assert.Equal(Documents[0].FilePath, d),
|
||||
d => Assert.Equal(Documents[2].FilePath, d));
|
||||
|
||||
Assert.Equal(ProjectChangeKind.DocumentsChanged, ProjectManager.ListenersNotifiedOf);
|
||||
Assert.Equal(ProjectChangeKind.DocumentRemoved, ProjectManager.ListenersNotifiedOf);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
|
|
@ -261,9 +306,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0]);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[1]);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[2]);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0], null);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[1], null);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[2], null);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Adding some computed state
|
||||
|
|
@ -284,9 +329,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0]);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[1]);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[2]);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0], null);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[1], null);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[2], null);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
|
|
@ -300,6 +345,126 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
Assert.Same(projectEngine, snapshot.GetProjectEngine());
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task DocumentOpened_UpdatesDocument()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0], null);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.DocumentOpened(HostProject.FilePath, Documents[0].FilePath, SourceText);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(ProjectChangeKind.DocumentChanged, ProjectManager.ListenersNotifiedOf);
|
||||
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
var text = await snapshot.GetDocument(Documents[0].FilePath).GetTextAsync();
|
||||
Assert.Same(SourceText, text);
|
||||
|
||||
Assert.True(ProjectManager.IsDocumentOpen(Documents[0].FilePath));
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task DocumentClosed_UpdatesDocument()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0], null);
|
||||
ProjectManager.DocumentOpened(HostProject.FilePath, Documents[0].FilePath, SourceText);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var expected = SourceText.From("Hi");
|
||||
var textAndVersion = TextAndVersion.Create(expected, VersionStamp.Create());
|
||||
|
||||
Assert.True(ProjectManager.IsDocumentOpen(Documents[0].FilePath));
|
||||
|
||||
// Act
|
||||
ProjectManager.DocumentClosed(HostProject.FilePath, Documents[0].FilePath, TextLoader.From(textAndVersion));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(ProjectChangeKind.DocumentChanged, ProjectManager.ListenersNotifiedOf);
|
||||
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
var text = await snapshot.GetDocument(Documents[0].FilePath).GetTextAsync();
|
||||
Assert.Same(expected, text);
|
||||
Assert.False(ProjectManager.IsDocumentOpen(Documents[0].FilePath));
|
||||
}
|
||||
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task DocumentClosed_AcceptsChange()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0], null);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var expected = SourceText.From("Hi");
|
||||
var textAndVersion = TextAndVersion.Create(expected, VersionStamp.Create());
|
||||
|
||||
// Act
|
||||
ProjectManager.DocumentClosed(HostProject.FilePath, Documents[0].FilePath, TextLoader.From(textAndVersion));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(ProjectChangeKind.DocumentChanged, ProjectManager.ListenersNotifiedOf);
|
||||
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
var text = await snapshot.GetDocument(Documents[0].FilePath).GetTextAsync();
|
||||
Assert.Same(expected, text);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task DocumentChanged_Snapshot_UpdatesDocument()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0], null);
|
||||
ProjectManager.DocumentOpened(HostProject.FilePath, Documents[0].FilePath, SourceText);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var expected = SourceText.From("Hi");
|
||||
|
||||
// Act
|
||||
ProjectManager.DocumentChanged(HostProject.FilePath, Documents[0].FilePath, expected);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(ProjectChangeKind.DocumentChanged, ProjectManager.ListenersNotifiedOf);
|
||||
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
var text = await snapshot.GetDocument(Documents[0].FilePath).GetTextAsync();
|
||||
Assert.Same(expected, text);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task DocumentChanged_Loader_UpdatesDocument()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.DocumentAdded(HostProject, Documents[0], null);
|
||||
ProjectManager.DocumentOpened(HostProject.FilePath, Documents[0].FilePath, SourceText);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var expected = SourceText.From("Hi");
|
||||
var textAndVersion = TextAndVersion.Create(expected, VersionStamp.Create());
|
||||
|
||||
// Act
|
||||
ProjectManager.DocumentChanged(HostProject.FilePath, Documents[0].FilePath, TextLoader.From(textAndVersion));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(ProjectChangeKind.DocumentChanged, ProjectManager.ListenersNotifiedOf);
|
||||
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
var text = await snapshot.GetDocument(Documents[0].FilePath).GetTextAsync();
|
||||
Assert.Same(expected, text);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void HostProjectAdded_WithoutWorkspaceProject_NotifiesListeners()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,12 +3,13 @@
|
|||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
public class WorkspaceProjectSnapshotChangeTriggerTest
|
||||
public class WorkspaceProjectSnapshotChangeTriggerTest : ForegroundDispatcherTestBase
|
||||
{
|
||||
public WorkspaceProjectSnapshotChangeTriggerTest()
|
||||
{
|
||||
|
|
@ -73,7 +74,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
private Workspace Workspace { get; }
|
||||
|
||||
[Theory]
|
||||
[ForegroundTheory]
|
||||
[InlineData(WorkspaceChangeKind.SolutionAdded)]
|
||||
[InlineData(WorkspaceChangeKind.SolutionChanged)]
|
||||
[InlineData(WorkspaceChangeKind.SolutionCleared)]
|
||||
|
|
@ -99,7 +100,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
p => Assert.Equal(ProjectNumberTwo.Id, p.WorkspaceProject.Id));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ForegroundTheory]
|
||||
[InlineData(WorkspaceChangeKind.SolutionAdded)]
|
||||
[InlineData(WorkspaceChangeKind.SolutionChanged)]
|
||||
[InlineData(WorkspaceChangeKind.SolutionCleared)]
|
||||
|
|
@ -131,13 +132,17 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
p => Assert.Equal(ProjectNumberTwo.Id, p.WorkspaceProject.Id));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ForegroundTheory]
|
||||
[InlineData(WorkspaceChangeKind.ProjectChanged)]
|
||||
[InlineData(WorkspaceChangeKind.ProjectReloaded)]
|
||||
public void WorkspaceChanged_ProjectChangeEvents_UpdatesProject(WorkspaceChangeKind kind)
|
||||
public async Task WorkspaceChanged_ProjectChangeEvents_UpdatesProject_AfterDelay(WorkspaceChangeKind kind)
|
||||
{
|
||||
// Arrange
|
||||
var trigger = new WorkspaceProjectSnapshotChangeTrigger();
|
||||
var trigger = new WorkspaceProjectSnapshotChangeTrigger()
|
||||
{
|
||||
ProjectChangeDelay = 50,
|
||||
};
|
||||
|
||||
var projectManager = new TestProjectSnapshotManager(new[] { trigger }, Workspace);
|
||||
projectManager.HostProjectAdded(HostProjectOne);
|
||||
projectManager.HostProjectAdded(HostProjectTwo);
|
||||
|
|
@ -153,6 +158,12 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
trigger.Workspace_WorkspaceChanged(Workspace, e);
|
||||
|
||||
// Assert
|
||||
//
|
||||
// The change hasn't come through yet.
|
||||
Assert.Equal("One", projectManager.Projects.Single().WorkspaceProject.AssemblyName);
|
||||
|
||||
await trigger._deferredUpdates.Single().Value;
|
||||
|
||||
Assert.Collection(
|
||||
projectManager.Projects.OrderBy(p => p.WorkspaceProject.Name),
|
||||
p =>
|
||||
|
|
@ -163,7 +174,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
p => Assert.Equal(ProjectNumberTwo.Id, p.WorkspaceProject.Id));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ForegroundFact]
|
||||
public void WorkspaceChanged_ProjectRemovedEvent_RemovesProject()
|
||||
{
|
||||
// Arrange
|
||||
|
|
@ -189,7 +200,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
p => Assert.Equal(ProjectNumberTwo.Id, p.WorkspaceProject.Id));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ForegroundFact]
|
||||
public void WorkspaceChanged_ProjectAddedEvent_AddsProject()
|
||||
{
|
||||
// Arrange
|
||||
|
|
@ -92,8 +92,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
|
||||
var projectSnapshots = new[]
|
||||
{
|
||||
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeProject, SomeWorkspaceProject)),
|
||||
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeOtherProject, SomeOtherWorkspaceProject)),
|
||||
new DefaultProjectSnapshot(ProjectState.Create(Workspace.Services, SomeProject, SomeWorkspaceProject)),
|
||||
new DefaultProjectSnapshot(ProjectState.Create(Workspace.Services, SomeOtherProject, SomeOtherWorkspaceProject)),
|
||||
};
|
||||
|
||||
var called = false;
|
||||
|
|
@ -140,8 +140,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
|
||||
var projectSnapshots = new[]
|
||||
{
|
||||
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeProject, null)),
|
||||
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeOtherProject, SomeOtherWorkspaceProject)),
|
||||
new DefaultProjectSnapshot(ProjectState.Create(Workspace.Services, SomeProject, null)),
|
||||
new DefaultProjectSnapshot(ProjectState.Create(Workspace.Services, SomeOtherProject, SomeOtherWorkspaceProject)),
|
||||
};
|
||||
|
||||
var projectManager = new Mock<ProjectSnapshotManagerBase>();
|
||||
|
|
@ -183,8 +183,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
|
||||
var projectSnapshots = new[]
|
||||
{
|
||||
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeProject, SomeWorkspaceProject)),
|
||||
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeOtherProject, SomeOtherWorkspaceProject)),
|
||||
new DefaultProjectSnapshot(ProjectState.Create(Workspace.Services, SomeProject, SomeWorkspaceProject)),
|
||||
new DefaultProjectSnapshot(ProjectState.Create(Workspace.Services, SomeOtherProject, SomeOtherWorkspaceProject)),
|
||||
};
|
||||
|
||||
var projectManager = new Mock<ProjectSnapshotManagerBase>();
|
||||
|
|
|
|||
|
|
@ -62,8 +62,8 @@ namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor
|
|||
|
||||
var projectSnapshots = new[]
|
||||
{
|
||||
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeProject, SomeWorkspaceProject)),
|
||||
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeOtherProject, SomeOtherWorkspaceProject)),
|
||||
new DefaultProjectSnapshot(ProjectState.Create(Workspace.Services, SomeProject, SomeWorkspaceProject)),
|
||||
new DefaultProjectSnapshot(ProjectState.Create(Workspace.Services, SomeOtherProject, SomeOtherWorkspaceProject)),
|
||||
};
|
||||
|
||||
var projectManager = new Mock<ProjectSnapshotManagerBase>(MockBehavior.Strict);
|
||||
|
|
@ -93,8 +93,8 @@ namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor
|
|||
var args = new BuildEventArgs(monitor: null, success: true);
|
||||
var projectSnapshots = new[]
|
||||
{
|
||||
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeProject, null)),
|
||||
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeOtherProject, SomeOtherWorkspaceProject)),
|
||||
new DefaultProjectSnapshot(ProjectState.Create(Workspace.Services, SomeProject, null)),
|
||||
new DefaultProjectSnapshot(ProjectState.Create(Workspace.Services, SomeOtherProject, SomeOtherWorkspaceProject)),
|
||||
};
|
||||
|
||||
var projectManager = new Mock<ProjectSnapshotManagerBase>();
|
||||
|
|
@ -121,8 +121,8 @@ namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor
|
|||
var args = new BuildEventArgs(monitor: null, success: true);
|
||||
var projectSnapshots = new[]
|
||||
{
|
||||
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeProject, null)),
|
||||
new DefaultProjectSnapshot(new ProjectState(Workspace.Services, SomeOtherProject, SomeOtherWorkspaceProject)),
|
||||
new DefaultProjectSnapshot(ProjectState.Create(Workspace.Services, SomeProject, null)),
|
||||
new DefaultProjectSnapshot(ProjectState.Create(Workspace.Services, SomeOtherProject, SomeOtherWorkspaceProject)),
|
||||
};
|
||||
|
||||
var projectManager = new Mock<ProjectSnapshotManagerBase>();
|
||||
|
|
|
|||
|
|
@ -76,19 +76,22 @@
|
|||
<Compile Include="Behaviors\ItemSelectedBehavior.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Properties\BindingRedirectAttributes.cs" />
|
||||
<Compile Include="RazorInfo\DirectiveDescriptorViewModel.cs" />
|
||||
<Compile Include="RazorInfo\DocumentSnapshotViewModel.cs" />
|
||||
<Compile Include="RazorInfo\DirectiveCollectionViewModel.cs" />
|
||||
<Compile Include="RazorInfo\DirectiveItemViewModel.cs" />
|
||||
<Compile Include="RazorInfo\DocumentCollectionViewModel.cs" />
|
||||
<Compile Include="RazorInfo\DocumentItemViewModel.cs" />
|
||||
<Compile Include="NotifyPropertyChanged.cs" />
|
||||
<Compile Include="RazorInfo\ProjectSnapshotViewModel.cs" />
|
||||
<Compile Include="RazorInfo\NullToEnabledConverter.cs" />
|
||||
<Compile Include="RazorInfo\ProjectViewModel.cs" />
|
||||
<Compile Include="RazorInfo\PropertyViewModel.cs" />
|
||||
<Compile Include="RazorInfo\ProjectPropertyItemViewModel.cs" />
|
||||
<Compile Include="RazorInfo\RazorInfoToolWindow.cs" />
|
||||
<Compile Include="RazorInfo\RazorInfoToolWindowCommand.cs" />
|
||||
<Compile Include="RazorInfo\RazorInfoViewModel.cs" />
|
||||
<Compile Include="RazorInfo\TagHelperCollectionViewModel.cs" />
|
||||
<Compile Include="RazorPackage.cs" />
|
||||
<Compile Include="RazorInfo\ProjectInfoViewModel.cs" />
|
||||
<Compile Include="RazorInfo\ProjectPropertyCollectionViewModel.cs" />
|
||||
<Compile Include="RelayCommand.cs" />
|
||||
<Compile Include="RazorInfo\TagHelperViewModel.cs" />
|
||||
<Compile Include="RazorInfo\TagHelperItemViewModel.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="razorLanguageService.servicehub.service.json">
|
||||
|
|
@ -230,7 +233,7 @@
|
|||
-->
|
||||
<PackageReference Include="Microsoft.VisualStudio.ComponentModelHost" Version="15.0.26606" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Shell.15.0" Version="15.0.26606" />
|
||||
<PackageReference Include="Microsoft.VSSDK.BuildTools" Version="15.1.192" />
|
||||
<PackageReference Include="Microsoft.VSSDK.BuildTools" Version="15.7.109" />
|
||||
</ItemGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<SuppressFromVsix>
|
||||
|
|
@ -268,7 +271,6 @@
|
|||
<Target Name="CopySymbolsToOutput" AfterTargets="Build" Condition="'$(SymbolsPublishDir)' != ''">
|
||||
<Copy SourceFiles="$(OutDir)$(AssemblyName).pdb" DestinationFolder="$(SymbolsPublishDir)" />
|
||||
</Target>
|
||||
|
||||
<!-- Include Razor SDK design time assets in the VSIX -->
|
||||
<ItemGroup>
|
||||
<Content Include="..\..\src\Microsoft.NET.Sdk.Razor\build\netstandard2.0\Microsoft.NET.Sdk.Razor.DesignTime.targets">
|
||||
|
|
@ -276,15 +278,12 @@
|
|||
<InstallRoot>MSBuild</InstallRoot>
|
||||
<VSIXSubPath>Microsoft\VisualStudio\Razor\</VSIXSubPath>
|
||||
</Content>
|
||||
|
||||
<Content Include="..\..\src\Microsoft.NET.Sdk.Razor\build\netstandard2.0\Rules\*.xaml">
|
||||
<IncludeInVsix>true</IncludeInVsix>
|
||||
<InstallRoot>MSBuild</InstallRoot>
|
||||
<VSIXSubPath>Microsoft\VisualStudio\Razor\Rules\</VSIXSubPath>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<!--
|
||||
Begin workaround for https://github.com/dotnet/sdk/issues/433
|
||||
|
||||
|
|
@ -310,7 +309,6 @@
|
|||
</Content>
|
||||
</ItemGroup>
|
||||
<!-- End workaround for https://github.com/dotnet/sdk/issues/433 -->
|
||||
|
||||
<Target Name="GetBuildVersion" Outputs="$(VsixVersion)" />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="Exists('$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets')" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||
{
|
||||
public class DirectiveCollectionViewModel : NotifyPropertyChanged
|
||||
{
|
||||
private readonly ProjectSnapshot _project;
|
||||
|
||||
internal DirectiveCollectionViewModel(ProjectSnapshot project)
|
||||
{
|
||||
_project = project;
|
||||
|
||||
Directives = new ObservableCollection<DirectiveItemViewModel>();
|
||||
|
||||
var feature = _project.GetProjectEngine().EngineFeatures.OfType<IRazorDirectiveFeature>().FirstOrDefault();
|
||||
foreach (var directive in feature?.Directives ?? Array.Empty<DirectiveDescriptor>())
|
||||
{
|
||||
Directives.Add(new DirectiveItemViewModel(directive));
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<DirectiveItemViewModel> Directives { get; }
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -7,11 +7,11 @@ using Microsoft.AspNetCore.Razor.Language;
|
|||
|
||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||
{
|
||||
public class DirectiveDescriptorViewModel : NotifyPropertyChanged
|
||||
public class DirectiveItemViewModel : NotifyPropertyChanged
|
||||
{
|
||||
private readonly DirectiveDescriptor _directive;
|
||||
|
||||
internal DirectiveDescriptorViewModel(DirectiveDescriptor directive)
|
||||
internal DirectiveItemViewModel(DirectiveDescriptor directive)
|
||||
{
|
||||
_directive = directive;
|
||||
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||
{
|
||||
public class DocumentCollectionViewModel : NotifyPropertyChanged
|
||||
{
|
||||
private readonly ProjectSnapshotManager _projectManager;
|
||||
private readonly Action<Exception> _errorHandler;
|
||||
|
||||
private ProjectSnapshot _project;
|
||||
|
||||
internal DocumentCollectionViewModel(ProjectSnapshotManager projectManager, ProjectSnapshot project, Action<Exception> errorHandler)
|
||||
{
|
||||
_projectManager = projectManager;
|
||||
_project = project;
|
||||
_errorHandler = errorHandler;
|
||||
|
||||
Documents = new ObservableCollection<DocumentItemViewModel>();
|
||||
|
||||
foreach (var filePath in project.DocumentFilePaths)
|
||||
{
|
||||
Documents.Add(new DocumentItemViewModel(projectManager, project.GetDocument(filePath), _errorHandler));
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<DocumentItemViewModel> Documents { get; }
|
||||
|
||||
internal void OnChange(ProjectChangeEventArgs e)
|
||||
{
|
||||
switch (e.Kind)
|
||||
{
|
||||
case ProjectChangeKind.DocumentAdded:
|
||||
{
|
||||
_project = _projectManager.GetLoadedProject(e.ProjectFilePath);
|
||||
Documents.Add(new DocumentItemViewModel(_projectManager, _project.GetDocument(e.DocumentFilePath), _errorHandler));
|
||||
break;
|
||||
}
|
||||
|
||||
case ProjectChangeKind.DocumentRemoved:
|
||||
{
|
||||
_project = _projectManager.GetLoadedProject(e.ProjectFilePath);
|
||||
|
||||
for (var i = Documents.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (Documents[i].FilePath == e.DocumentFilePath)
|
||||
{
|
||||
Documents.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ProjectChangeKind.DocumentChanged:
|
||||
{
|
||||
_project = _projectManager.GetLoadedProject(e.ProjectFilePath);
|
||||
for (var i = Documents.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (Documents[i].FilePath == e.DocumentFilePath)
|
||||
{
|
||||
Documents[i] = new DocumentItemViewModel(_projectManager, _project.GetDocument(e.DocumentFilePath), _errorHandler);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||
{
|
||||
public class DocumentItemViewModel : NotifyPropertyChanged
|
||||
{
|
||||
private readonly ProjectSnapshotManager _snapshotManager;
|
||||
private readonly DocumentSnapshot _document;
|
||||
private readonly Action<Exception> _errorHandler;
|
||||
|
||||
private Visibility _progressVisibility;
|
||||
|
||||
internal DocumentItemViewModel(ProjectSnapshotManager snapshotManager, DocumentSnapshot document, Action<Exception> errorHandler)
|
||||
{
|
||||
_snapshotManager = snapshotManager;
|
||||
_document = document;
|
||||
_errorHandler = errorHandler;
|
||||
|
||||
InitializeGeneratedDocument();
|
||||
}
|
||||
|
||||
public string FilePath => _document.FilePath;
|
||||
|
||||
public string StatusText => _snapshotManager.IsDocumentOpen(_document.FilePath) ? "Open" : "Closed";
|
||||
|
||||
public string TargetPath => _document.TargetPath;
|
||||
|
||||
public Visibility ProgressVisibility
|
||||
{
|
||||
get => _progressVisibility;
|
||||
set
|
||||
{
|
||||
_progressVisibility = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private async void InitializeGeneratedDocument()
|
||||
{
|
||||
ProgressVisibility = Visibility.Hidden;
|
||||
|
||||
try
|
||||
{
|
||||
if (!_document.TryGetGeneratedOutput(out var result))
|
||||
{
|
||||
ProgressVisibility = Visibility.Visible;
|
||||
await _document.GetGeneratedOutputAsync();
|
||||
await Task.Delay(250); // Force a delay for the UI
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_errorHandler(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ProgressVisibility = Visibility.Hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||
{
|
||||
public class DocumentSnapshotViewModel : NotifyPropertyChanged
|
||||
{
|
||||
private double _progress;
|
||||
|
||||
internal DocumentSnapshotViewModel(DocumentSnapshot document)
|
||||
{
|
||||
Document = document;
|
||||
|
||||
InitializeGeneratedDocument();
|
||||
}
|
||||
|
||||
internal DocumentSnapshot Document { get; }
|
||||
|
||||
public string FilePath => Document.FilePath;
|
||||
|
||||
public string TargetPath => Document.TargetPath;
|
||||
|
||||
public bool CodeGenerationInProgress => _progress < 100;
|
||||
|
||||
public double CodeGenerationProgress => _progress;
|
||||
|
||||
private async void InitializeGeneratedDocument()
|
||||
{
|
||||
_progress = 0;
|
||||
OnPropertyChanged(nameof(CodeGenerationInProgress));
|
||||
OnPropertyChanged(nameof(CodeGenerationProgress));
|
||||
|
||||
try
|
||||
{
|
||||
await Document.GetGeneratedOutputAsync();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_progress = 100;
|
||||
OnPropertyChanged(nameof(CodeGenerationInProgress));
|
||||
OnPropertyChanged(nameof(CodeGenerationProgress));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||
{
|
||||
public class NullToEnabledConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (targetType == typeof(bool))
|
||||
{
|
||||
return value != null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows;
|
||||
|
||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||
{
|
||||
public class ProjectInfoViewModel : NotifyPropertyChanged
|
||||
{
|
||||
private ObservableCollection<DirectiveDescriptorViewModel> _directives;
|
||||
private ObservableCollection<DocumentSnapshotViewModel> _documents;
|
||||
private ObservableCollection<TagHelperViewModel> _tagHelpers;
|
||||
private bool _tagHelpersLoading;
|
||||
|
||||
public ObservableCollection<DirectiveDescriptorViewModel> Directives
|
||||
{
|
||||
get { return _directives; }
|
||||
set
|
||||
{
|
||||
_directives = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<DocumentSnapshotViewModel> Documents
|
||||
{
|
||||
get { return _documents; }
|
||||
set
|
||||
{
|
||||
_documents = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<TagHelperViewModel> TagHelpers
|
||||
{
|
||||
get { return _tagHelpers; }
|
||||
set
|
||||
{
|
||||
_tagHelpers = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public bool TagHelpersLoading
|
||||
{
|
||||
get { return _tagHelpersLoading; }
|
||||
set
|
||||
{
|
||||
_tagHelpersLoading = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(TagHelperProgressVisibility));
|
||||
}
|
||||
}
|
||||
|
||||
public Visibility TagHelperProgressVisibility => TagHelpersLoading ? Visibility.Visible : Visibility.Hidden;
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||
{
|
||||
public class ProjectPropertyCollectionViewModel : NotifyPropertyChanged
|
||||
{
|
||||
private readonly ProjectSnapshot _project;
|
||||
|
||||
internal ProjectPropertyCollectionViewModel(ProjectSnapshot project)
|
||||
{
|
||||
_project = project;
|
||||
|
||||
Properties = new ObservableCollection<ProjectPropertyItemViewModel>();
|
||||
Properties.Add(new ProjectPropertyItemViewModel("Language Version", _project.Configuration?.LanguageVersion.ToString()));
|
||||
Properties.Add(new ProjectPropertyItemViewModel("Configuration", FormatConfiguration(_project)));
|
||||
Properties.Add(new ProjectPropertyItemViewModel("Extensions", FormatExtensions(_project)));
|
||||
Properties.Add(new ProjectPropertyItemViewModel("Workspace Project", _project.WorkspaceProject?.Name));
|
||||
}
|
||||
|
||||
public ObservableCollection<ProjectPropertyItemViewModel> Properties { get; }
|
||||
|
||||
private static string FormatConfiguration(ProjectSnapshot project)
|
||||
{
|
||||
return $"{project.Configuration.ConfigurationName} ({project.Configuration.GetType().Name})";
|
||||
}
|
||||
|
||||
private static string FormatExtensions(ProjectSnapshot project)
|
||||
{
|
||||
return $"{string.Join(", ", project.Configuration.Extensions.Select(e => e.ExtensionName))}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -5,9 +5,9 @@
|
|||
|
||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||
{
|
||||
public class PropertyViewModel : NotifyPropertyChanged
|
||||
public class ProjectPropertyItemViewModel : NotifyPropertyChanged
|
||||
{
|
||||
internal PropertyViewModel(string name, string value)
|
||||
internal ProjectPropertyItemViewModel(string name, string value)
|
||||
{
|
||||
Name = name;
|
||||
Value = value;
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||
{
|
||||
public class ProjectSnapshotViewModel : NotifyPropertyChanged
|
||||
{
|
||||
internal ProjectSnapshotViewModel(ProjectSnapshot project)
|
||||
{
|
||||
Project = project;
|
||||
|
||||
Id = project.WorkspaceProject?.Id;
|
||||
Properties = new ObservableCollection<PropertyViewModel>();
|
||||
|
||||
InitializeProperties();
|
||||
}
|
||||
|
||||
internal ProjectSnapshot Project { get; }
|
||||
|
||||
public string Name => Path.GetFileNameWithoutExtension(Project.FilePath);
|
||||
|
||||
public ProjectId Id { get; }
|
||||
|
||||
public ObservableCollection<PropertyViewModel> Properties { get; }
|
||||
|
||||
private void InitializeProperties()
|
||||
{
|
||||
Properties.Clear();
|
||||
|
||||
Properties.Add(new PropertyViewModel("Language Version", Project.Configuration?.LanguageVersion.ToString()));
|
||||
Properties.Add(new PropertyViewModel("Configuration", FormatConfiguration(Project)));
|
||||
Properties.Add(new PropertyViewModel("Extensions", FormatExtensions(Project)));
|
||||
Properties.Add(new PropertyViewModel("Workspace Project", Project.WorkspaceProject?.Name));
|
||||
}
|
||||
|
||||
private static string FormatConfiguration(ProjectSnapshot project)
|
||||
{
|
||||
return $"{project.Configuration.ConfigurationName} ({project.Configuration.GetType().Name})";
|
||||
}
|
||||
|
||||
private static string FormatExtensions(ProjectSnapshot project)
|
||||
{
|
||||
return $"{string.Join(", ", project.Configuration.Extensions.Select(e => e.ExtensionName))}";
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -8,8 +8,6 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
{
|
||||
public class ProjectViewModel : NotifyPropertyChanged
|
||||
{
|
||||
private ProjectSnapshotViewModel _snapshot;
|
||||
|
||||
internal ProjectViewModel(string filePath)
|
||||
{
|
||||
FilePath = filePath;
|
||||
|
|
@ -18,19 +16,6 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
public string FilePath { get; }
|
||||
|
||||
public string Name => Path.GetFileNameWithoutExtension(FilePath);
|
||||
|
||||
public bool HasSnapshot => Snapshot != null;
|
||||
|
||||
public ProjectSnapshotViewModel Snapshot
|
||||
{
|
||||
get => _snapshot;
|
||||
set
|
||||
{
|
||||
_snapshot = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(HasSnapshot));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -42,18 +42,16 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
_projectManager = _workspace.Services.GetLanguageServices(RazorLanguage.Name).GetRequiredService<ProjectSnapshotManager>();
|
||||
_projectManager.Changed += ProjectManager_Changed;
|
||||
|
||||
DataContext = new RazorInfoViewModel(this, _workspace, _projectManager, OnException);
|
||||
DataContext = new RazorInfoViewModel(_workspace, _projectManager, OnException);
|
||||
|
||||
foreach (var project in _projectManager.Projects)
|
||||
{
|
||||
DataContext.Projects.Add(new ProjectViewModel(project.FilePath)
|
||||
{
|
||||
Snapshot = new ProjectSnapshotViewModel(project),
|
||||
});
|
||||
DataContext.Projects.Add(new ProjectViewModel(project.FilePath));
|
||||
}
|
||||
|
||||
if (DataContext.Projects.Count > 0)
|
||||
{
|
||||
DataContext.CurrentProject = DataContext.Projects[0];
|
||||
DataContext.SelectedProject = DataContext.Projects[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -69,70 +67,7 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
|
||||
private void ProjectManager_Changed(object sender, ProjectChangeEventArgs e)
|
||||
{
|
||||
switch (e.Kind)
|
||||
{
|
||||
case ProjectChangeKind.ProjectAdded:
|
||||
{
|
||||
var added = new ProjectViewModel(e.ProjectFilePath)
|
||||
{
|
||||
Snapshot = new ProjectSnapshotViewModel(_projectManager.GetLoadedProject(e.ProjectFilePath)),
|
||||
};
|
||||
|
||||
DataContext.Projects.Add(added);
|
||||
|
||||
if (DataContext.Projects.Count == 1)
|
||||
{
|
||||
DataContext.CurrentProject = added;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ProjectChangeKind.ProjectRemoved:
|
||||
{
|
||||
ProjectViewModel removed = null;
|
||||
for (var i = DataContext.Projects.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var project = DataContext.Projects[i];
|
||||
if (project.FilePath == e.ProjectFilePath)
|
||||
{
|
||||
removed = project;
|
||||
DataContext.Projects.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (DataContext.CurrentProject == removed)
|
||||
{
|
||||
DataContext.CurrentProject = null;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ProjectChangeKind.ProjectChanged:
|
||||
case ProjectChangeKind.DocumentsChanged:
|
||||
{
|
||||
ProjectViewModel changed = null;
|
||||
for (var i = DataContext.Projects.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var project = DataContext.Projects[i];
|
||||
if (project.FilePath == e.ProjectFilePath)
|
||||
{
|
||||
changed = project;
|
||||
changed.Snapshot = new ProjectSnapshotViewModel(_projectManager.GetLoadedProject(e.ProjectFilePath));
|
||||
DataContext.LoadProjectInfo();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ProjectChangeKind.DocumentContentChanged:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
DataContext.OnChange(e);
|
||||
}
|
||||
|
||||
private void OnException(Exception ex)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
d:DesignWidth="300"
|
||||
Name="RazorInfoToolWindow">
|
||||
<UserControl.Resources>
|
||||
<local:NullToEnabledConverter x:Key="EnabledConverter"/>
|
||||
<Style
|
||||
x:Key="{x:Type Button}">
|
||||
<Setter
|
||||
|
|
@ -101,7 +102,7 @@
|
|||
<ComboBox
|
||||
Grid.Column="1"
|
||||
ItemsSource="{Binding Projects}"
|
||||
SelectedValue="{Binding CurrentProject}">
|
||||
SelectedValue="{Binding SelectedProject}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel
|
||||
|
|
@ -123,10 +124,10 @@
|
|||
IsEnabled="False" />
|
||||
<Grid
|
||||
Grid.Row="2"
|
||||
IsEnabled="{Binding CurrentProject.HasSnapshot}"
|
||||
IsEnabled="{Binding ProjectProperties, Converter={StaticResource EnabledConverter}}"
|
||||
Height="150">
|
||||
<ListView
|
||||
ItemsSource="{Binding CurrentProject.Snapshot.Properties}">
|
||||
ItemsSource="{Binding ProjectProperties.Properties}">
|
||||
<ListView.View>
|
||||
<GridView
|
||||
AllowsColumnReorder="False">
|
||||
|
|
@ -166,7 +167,7 @@
|
|||
Grid.Row="3" />
|
||||
<Expander
|
||||
Grid.Row="4"
|
||||
IsEnabled="{Binding CurrentProjectInfo, TargetNullValue=False}">
|
||||
IsEnabled="{Binding TagHelpers, Converter={StaticResource EnabledConverter}}">
|
||||
<Expander.Header>
|
||||
<!-- The complicated binding here makes the header stretch horizontally -->
|
||||
<Grid
|
||||
|
|
@ -185,12 +186,12 @@
|
|||
Width="Auto"
|
||||
Height="20"
|
||||
Margin="5"
|
||||
Visibility="{Binding CurrentProjectInfo.TagHelperProgressVisibility, TargetNullValue=Visibility.Hidden}"
|
||||
Visibility="{Binding TagHelpers.ProgressVisibility, TargetNullValue=Hidden, FallbackValue=Hidden}"
|
||||
IsIndeterminate="True" />
|
||||
</Grid>
|
||||
</Expander.Header>
|
||||
<ListView
|
||||
ItemsSource="{Binding CurrentProjectInfo.TagHelpers}">
|
||||
ItemsSource="{Binding TagHelpers.TagHelpers}">
|
||||
<ListView.View>
|
||||
<GridView
|
||||
AllowsColumnReorder="False">
|
||||
|
|
@ -245,13 +246,13 @@
|
|||
Grid.Row="5" />
|
||||
<Expander
|
||||
Grid.Row="6"
|
||||
IsEnabled="{Binding CurrentProjectInfo, TargetNullValue=False}">
|
||||
IsEnabled="{Binding Directives, Converter={StaticResource EnabledConverter}}">
|
||||
<Expander.Header>
|
||||
<Label
|
||||
Content="Directives" />
|
||||
</Expander.Header>
|
||||
<ListView
|
||||
ItemsSource="{Binding CurrentProjectInfo.Directives}">
|
||||
ItemsSource="{Binding Directives.Directives}">
|
||||
<ListView.View>
|
||||
<GridView
|
||||
AllowsColumnReorder="False">
|
||||
|
|
@ -291,13 +292,13 @@
|
|||
Grid.Row="7" />
|
||||
<Expander
|
||||
Grid.Row="8"
|
||||
IsEnabled="{Binding CurrentProjectInfo, TargetNullValue=False}">
|
||||
IsEnabled="{Binding Documents, Converter={StaticResource EnabledConverter}}">
|
||||
<Expander.Header>
|
||||
<Label
|
||||
Content="Documents" />
|
||||
</Expander.Header>
|
||||
<ListView
|
||||
ItemsSource="{Binding CurrentProjectInfo.Documents}">
|
||||
ItemsSource="{Binding Documents.Documents}">
|
||||
<ListView.View>
|
||||
<GridView
|
||||
AllowsColumnReorder="False">
|
||||
|
|
@ -307,12 +308,17 @@
|
|||
Content="Status" />
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid
|
||||
HorizontalAlignment="{Binding HorizontalAlignment, RelativeSource={RelativeSource AncestorType=ContentPresenter}, Mode=OneWayToSource}">
|
||||
<ProgressBar
|
||||
Height="15"
|
||||
Width="40"
|
||||
IsIndeterminate="{Binding Path=CodeGenerationInProgress, Mode=OneWay}"
|
||||
Value="{Binding Path=CodeGenerationProgress, Mode=OneWay}" />
|
||||
IsIndeterminate="True"
|
||||
Visibility="{Binding Path=ProgressVisibility, FallbackValue=Hidden}" />
|
||||
<TextBlock
|
||||
TextWrapping="NoWrap"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
Text="{Binding Path=StatusText, Mode=OneWay}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
|
|
|
|||
|
|
@ -2,11 +2,10 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Windows.Input;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
|
|
@ -14,23 +13,19 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
{
|
||||
internal class RazorInfoViewModel : NotifyPropertyChanged
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly ProjectSnapshotManager _projectManager;
|
||||
private readonly Workspace _workspace;
|
||||
private readonly ProjectSnapshotManager _projectManager;
|
||||
private readonly Action<Exception> _errorHandler;
|
||||
|
||||
private DocumentSnapshotViewModel _currentDocument;
|
||||
private ProjectViewModel _currentProject;
|
||||
private ProjectInfoViewModel _currentProjectInfo;
|
||||
|
||||
private ProjectViewModel _selectedProject;
|
||||
private ProjectPropertyCollectionViewModel _projectProperties;
|
||||
private DirectiveCollectionViewModel _directives;
|
||||
private DocumentCollectionViewModel _documents;
|
||||
private TagHelperCollectionViewModel _tagHelpers;
|
||||
private ICommand _updateCommand;
|
||||
|
||||
public RazorInfoViewModel(
|
||||
IServiceProvider services,
|
||||
Workspace workspace,
|
||||
ProjectSnapshotManager projectManager,
|
||||
Action<Exception> errorHandler)
|
||||
public RazorInfoViewModel(Workspace workspace, ProjectSnapshotManager projectManager, Action<Exception> errorHandler)
|
||||
{
|
||||
_services = services;
|
||||
_workspace = workspace;
|
||||
_projectManager = projectManager;
|
||||
_errorHandler = errorHandler;
|
||||
|
|
@ -38,34 +33,56 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
UpdateCommand = new RelayCommand<object>(ExecuteUpdate, CanExecuteUpdate);
|
||||
}
|
||||
|
||||
public DocumentSnapshotViewModel CurrentDocument
|
||||
public ObservableCollection<ProjectViewModel> Projects { get; } = new ObservableCollection<ProjectViewModel>();
|
||||
|
||||
public ProjectViewModel SelectedProject
|
||||
{
|
||||
get { return _currentDocument; }
|
||||
get { return _selectedProject; }
|
||||
set
|
||||
{
|
||||
_currentDocument = value;
|
||||
_selectedProject = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
OnSelectedProjectChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public ProjectPropertyCollectionViewModel ProjectProperties
|
||||
{
|
||||
get { return _projectProperties; }
|
||||
set
|
||||
{
|
||||
_projectProperties = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public ProjectViewModel CurrentProject
|
||||
public DirectiveCollectionViewModel Directives
|
||||
{
|
||||
get { return _currentProject; }
|
||||
get { return _directives; }
|
||||
set
|
||||
{
|
||||
_currentProject = value;
|
||||
_directives = value;
|
||||
OnPropertyChanged();
|
||||
|
||||
LoadProjectInfo();
|
||||
}
|
||||
}
|
||||
|
||||
public ProjectInfoViewModel CurrentProjectInfo
|
||||
public DocumentCollectionViewModel Documents
|
||||
{
|
||||
get { return _currentProjectInfo; }
|
||||
get { return _documents; }
|
||||
set
|
||||
{
|
||||
_currentProjectInfo = value;
|
||||
_documents = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public TagHelperCollectionViewModel TagHelpers
|
||||
{
|
||||
get { return _tagHelpers; }
|
||||
set
|
||||
{
|
||||
_tagHelpers = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
|
@ -80,64 +97,113 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<ProjectViewModel> Projects { get; } = new ObservableCollection<ProjectViewModel>();
|
||||
public void OnChange(ProjectChangeEventArgs e)
|
||||
{
|
||||
switch (e.Kind)
|
||||
{
|
||||
case ProjectChangeKind.ProjectAdded:
|
||||
{
|
||||
var added = new ProjectViewModel(e.ProjectFilePath);
|
||||
Projects.Add(added);
|
||||
|
||||
if (Projects.Count == 1)
|
||||
{
|
||||
SelectedProject = added;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ProjectChangeKind.ProjectRemoved:
|
||||
{
|
||||
ProjectViewModel removed = null;
|
||||
for (var i = Projects.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var project = Projects[i];
|
||||
if (project.FilePath == e.ProjectFilePath)
|
||||
{
|
||||
removed = project;
|
||||
Projects.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (SelectedProject == removed)
|
||||
{
|
||||
SelectedProject = null;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ProjectChangeKind.ProjectChanged:
|
||||
{
|
||||
if (SelectedProject != null && SelectedProject.FilePath == e.ProjectFilePath)
|
||||
{
|
||||
OnSelectedProjectChanged();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ProjectChangeKind.DocumentAdded:
|
||||
case ProjectChangeKind.DocumentRemoved:
|
||||
case ProjectChangeKind.DocumentChanged:
|
||||
{
|
||||
if (SelectedProject != null && SelectedProject.FilePath == e.ProjectFilePath)
|
||||
{
|
||||
Documents?.OnChange(e);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSelectedProjectChanged()
|
||||
{
|
||||
if (SelectedProject == null)
|
||||
{
|
||||
ProjectProperties = null;
|
||||
Directives = null;
|
||||
Documents = null;
|
||||
TagHelpers = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var project = _projectManager.GetLoadedProject(_selectedProject.FilePath);
|
||||
ProjectProperties = new ProjectPropertyCollectionViewModel(project);
|
||||
Directives = new DirectiveCollectionViewModel(project);
|
||||
Documents = new DocumentCollectionViewModel(_projectManager, project, _errorHandler);
|
||||
TagHelpers = new TagHelperCollectionViewModel(project, _errorHandler);
|
||||
}
|
||||
|
||||
private bool CanExecuteUpdate(object state)
|
||||
{
|
||||
return CurrentProject?.Snapshot?.Project.WorkspaceProject != null;
|
||||
return SelectedProject != null;
|
||||
}
|
||||
|
||||
private void ExecuteUpdate(object state)
|
||||
{
|
||||
var projectId = CurrentProject?.Snapshot?.Project?.WorkspaceProject?.Id;
|
||||
if (projectId != null)
|
||||
{
|
||||
var solution = _workspace.CurrentSolution;
|
||||
var project = solution.GetProject(projectId);
|
||||
if (project != null)
|
||||
{
|
||||
((ProjectSnapshotManagerBase)_projectManager).WorkspaceProjectChanged(project);
|
||||
}
|
||||
}
|
||||
}
|
||||
public async void LoadProjectInfo()
|
||||
{
|
||||
CurrentProjectInfo = new ProjectInfoViewModel();
|
||||
|
||||
var snapshot = CurrentProject?.Snapshot.Project;
|
||||
if (snapshot == null)
|
||||
var projectFilePath = SelectedProject?.FilePath;
|
||||
if (projectFilePath == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var projectEngine = snapshot.GetProjectEngine();
|
||||
var feature = projectEngine.EngineFeatures.OfType<IRazorDirectiveFeature>().FirstOrDefault();
|
||||
var directives = feature?.Directives ?? Array.Empty<DirectiveDescriptor>();
|
||||
CurrentProjectInfo.Directives = new ObservableCollection<DirectiveDescriptorViewModel>(directives.Select(d => new DirectiveDescriptorViewModel(d)));
|
||||
|
||||
var documents = snapshot.DocumentFilePaths.Select(d => snapshot.GetDocument(d));
|
||||
CurrentProjectInfo.Documents = new ObservableCollection<DocumentSnapshotViewModel>(documents.Select(d => new DocumentSnapshotViewModel(d)));
|
||||
|
||||
if (snapshot.TryGetTagHelpers(out var tagHelpers))
|
||||
var project = _projectManager.GetLoadedProject(projectFilePath);
|
||||
if (project != null && project.WorkspaceProject != null)
|
||||
{
|
||||
CurrentProjectInfo.TagHelpers = new ObservableCollection<TagHelperViewModel>(tagHelpers.Select(t => new TagHelperViewModel(t)));
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentProjectInfo.TagHelpers = new ObservableCollection<TagHelperViewModel>();
|
||||
CurrentProjectInfo.TagHelpersLoading = true;
|
||||
|
||||
try
|
||||
var solution = _workspace.CurrentSolution;
|
||||
var workspaceProject = solution.GetProject(project.WorkspaceProject.Id);
|
||||
if (workspaceProject != null)
|
||||
{
|
||||
tagHelpers = await snapshot.GetTagHelpersAsync();
|
||||
CurrentProjectInfo.TagHelpers = new ObservableCollection<TagHelperViewModel>(tagHelpers.Select(t => new TagHelperViewModel(t)));
|
||||
}
|
||||
finally
|
||||
{
|
||||
CurrentProjectInfo.TagHelpersLoading = false;
|
||||
((ProjectSnapshotManagerBase)_projectManager).WorkspaceProjectChanged(workspaceProject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||
{
|
||||
public class TagHelperCollectionViewModel : NotifyPropertyChanged
|
||||
{
|
||||
private readonly ProjectSnapshot _project;
|
||||
private readonly Action<Exception> _errorHandler;
|
||||
|
||||
private Visibility _progressVisibility;
|
||||
|
||||
internal TagHelperCollectionViewModel(ProjectSnapshot project, Action<Exception> errorHandler)
|
||||
{
|
||||
_project = project;
|
||||
_errorHandler = errorHandler;
|
||||
|
||||
TagHelpers = new ObservableCollection<TagHelperItemViewModel>();
|
||||
InitializeTagHelpers();
|
||||
}
|
||||
|
||||
public ObservableCollection<TagHelperItemViewModel> TagHelpers { get; }
|
||||
|
||||
public Visibility ProgressVisibility
|
||||
{
|
||||
get => _progressVisibility;
|
||||
set
|
||||
{
|
||||
_progressVisibility = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private async void InitializeTagHelpers()
|
||||
{
|
||||
ProgressVisibility = Visibility.Hidden;
|
||||
|
||||
try
|
||||
{
|
||||
if (!_project.TryGetTagHelpers(out var tagHelpers))
|
||||
{
|
||||
ProgressVisibility = Visibility.Visible;
|
||||
tagHelpers = await _project.GetTagHelpersAsync();
|
||||
await Task.Delay(250); // Force a delay for the UI
|
||||
}
|
||||
|
||||
foreach (var tagHelper in tagHelpers)
|
||||
{
|
||||
TagHelpers.Add(new TagHelperItemViewModel(tagHelper));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_errorHandler(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ProgressVisibility = Visibility.Hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||
{
|
||||
public class TagHelperItemViewModel : NotifyPropertyChanged
|
||||
{
|
||||
private readonly TagHelperDescriptor _tagHelper;
|
||||
|
||||
internal TagHelperItemViewModel(TagHelperDescriptor tagHelper)
|
||||
{
|
||||
_tagHelper = tagHelper;
|
||||
}
|
||||
|
||||
public string AssemblyName => _tagHelper.AssemblyName;
|
||||
|
||||
public string TargetElement => string.Join(", ", _tagHelper.TagMatchingRules.Select(rule => rule.TagName));
|
||||
|
||||
public string TypeName => _tagHelper.GetTypeName();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||
{
|
||||
public class TagHelperViewModel : NotifyPropertyChanged
|
||||
{
|
||||
private readonly TagHelperDescriptor _descriptor;
|
||||
|
||||
internal TagHelperViewModel(TagHelperDescriptor descriptor)
|
||||
{
|
||||
_descriptor = descriptor;
|
||||
}
|
||||
|
||||
public string AssemblyName => _descriptor.AssemblyName;
|
||||
|
||||
public string TargetElement => string.Join(", ", _descriptor.TagMatchingRules.Select(rule => rule.TagName));
|
||||
|
||||
public string TypeName => _descriptor.GetTypeName();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Loading…
Reference in New Issue