Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,64 +15,37 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer;

internal class DefaultDocumentContextFactory : DocumentContextFactory
{
private readonly ProjectSnapshotManagerDispatcher _projectSnapshotManagerDispatcher;
private readonly ISnapshotResolver _snapshotResolver;
private readonly DocumentVersionCache _documentVersionCache;
private readonly ILogger<DefaultDocumentContextFactory> _logger;

public DefaultDocumentContextFactory(
ProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher,
ISnapshotResolver snapshotResolver,
DocumentVersionCache documentVersionCache,
ILoggerFactory loggerFactory)
{
_projectSnapshotManagerDispatcher = projectSnapshotManagerDispatcher;
_snapshotResolver = snapshotResolver;
_documentVersionCache = documentVersionCache;
_logger = loggerFactory.CreateLogger<DefaultDocumentContextFactory>();
}

protected override async Task<DocumentContext?> TryCreateCoreAsync(Uri documentUri, VSProjectContext? projectContext, bool versioned, CancellationToken cancellationToken)
protected override Task<DocumentContext?> TryCreateCoreAsync(Uri documentUri, VSProjectContext? projectContext, bool versioned, CancellationToken cancellationToken)
{
var filePath = documentUri.GetAbsoluteOrUNCPath();

var documentAndVersion = await _projectSnapshotManagerDispatcher.RunOnDispatcherThreadAsync(() =>
{
// TODO: Use project context to resolve the document.
if (_snapshotResolver.TryResolveDocument(filePath, out var documentSnapshot))
{
if (!versioned)
{
return new DocumentSnapshotAndVersion(documentSnapshot, Version: null);
}

if (_documentVersionCache.TryGetDocumentVersion(documentSnapshot, out var version))
{
return new DocumentSnapshotAndVersion(documentSnapshot, version.Value);
}
}

// This is super rare, if we get here it could mean many things. Some of which:
// 1. Stale request:
// - Got queued after a "document closed" / "document removed" type action
// - Took too long to run and by the time the request needed the document context the
// version cache has evicted the entry
// 2. Client is misbehaving and sending requests for a document that we've never seen before.
_logger.LogWarning("Tried to create context for document {documentUri} which was not found.", documentUri);
return null;
}, cancellationToken).ConfigureAwait(false);
var documentAndVersion = TryGetDocumentAndVersion(filePath, versioned);

if (documentAndVersion is null)
{
// Stale request or misbehaving client, see above comment.
return null;
return Task.FromResult<DocumentContext?>(null);
}

var (documentSnapshot, version) = documentAndVersion;
if (documentSnapshot is null)
{
Debug.Fail($"Document snapshot should never be null here for '{filePath}'. This indicates that our acquisition of documents / versions did not behave as expected.");
return null;
return Task.FromResult<DocumentContext?>(null);
}

cancellationToken.ThrowIfCancellationRequested();
Expand All @@ -82,13 +55,41 @@ public DefaultDocumentContextFactory(
// If we were asked for a versioned document, but have no version info, then we didn't find the document
if (version is null)
{
return null;
return Task.FromResult<DocumentContext?>(null);
}

return new VersionedDocumentContext(documentUri, documentSnapshot, projectContext, version.Value);
return Task.FromResult<DocumentContext?>(
new VersionedDocumentContext(documentUri, documentSnapshot, projectContext, version.Value));
}

return Task.FromResult<DocumentContext?>(
new DocumentContext(documentUri, documentSnapshot, projectContext));
}

private DocumentSnapshotAndVersion? TryGetDocumentAndVersion(string filePath, bool versioned)
{
// TODO: Use project context to resolve the document.
if (_snapshotResolver.TryResolveDocument(filePath, out var documentSnapshot))
{
if (!versioned)
{
return new DocumentSnapshotAndVersion(documentSnapshot, Version: null);
}

if (_documentVersionCache.TryGetDocumentVersion(documentSnapshot, out var version))
{
return new DocumentSnapshotAndVersion(documentSnapshot, version.Value);
}
}

return new DocumentContext(documentUri, documentSnapshot, projectContext);
// This is super rare, if we get here it could mean many things. Some of which:
// 1. Stale request:
// - Got queued after a "document closed" / "document removed" type action
// - Took too long to run and by the time the request needed the document context the
// version cache has evicted the entry
// 2. Client is misbehaving and sending requests for a document that we've never seen before.
_logger.LogWarning("Tried to create context for document {filePath} which was not found.", filePath);
return null;
}

private record DocumentSnapshotAndVersion(IDocumentSnapshot Snapshot, int? Version);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer;

internal class DefaultRazorComponentSearchEngine : RazorComponentSearchEngine
{
private readonly ProjectSnapshotManagerDispatcher _projectSnapshotManagerDispatcher;
private readonly ProjectSnapshotManager _projectSnapshotManager;
private readonly ILogger<DefaultRazorComponentSearchEngine> _logger;

public DefaultRazorComponentSearchEngine(
ProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher,
ProjectSnapshotManagerAccessor projectSnapshotManagerAccessor,
ILoggerFactory loggerFactory)
{
Expand All @@ -30,7 +28,6 @@ public DefaultRazorComponentSearchEngine(
throw new ArgumentNullException(nameof(loggerFactory));
}

_projectSnapshotManagerDispatcher = projectSnapshotManagerDispatcher ?? throw new ArgumentNullException(nameof(projectSnapshotManagerDispatcher));
_projectSnapshotManager = projectSnapshotManagerAccessor?.Instance ?? throw new ArgumentNullException(nameof(projectSnapshotManagerAccessor));
_logger = loggerFactory.CreateLogger<DefaultRazorComponentSearchEngine>();
}
Expand All @@ -49,9 +46,7 @@ public DefaultRazorComponentSearchEngine(
return null;
}

var projects = await _projectSnapshotManagerDispatcher.RunOnDispatcherThreadAsync(
() => _projectSnapshotManager.GetProjects().ToArray(),
cancellationToken).ConfigureAwait(false);
var projects = _projectSnapshotManager.GetProjects();

foreach (var project in projects)
{
Expand Down Expand Up @@ -110,9 +105,7 @@ public DefaultRazorComponentSearchEngine(

var lookupSymbolName = RemoveGenericContent(typeName.AsMemory());

var projects = await _projectSnapshotManagerDispatcher.RunOnDispatcherThreadAsync(
() => _projectSnapshotManager.GetProjects(),
CancellationToken.None).ConfigureAwait(false);
var projects = _projectSnapshotManager.GetProjects();

foreach (var project in projects)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public async Task TryCreateAsync_CanNotResolveDocument_ReturnsNull()
{
// Arrange
var uri = new Uri("C:/path/to/file.cshtml");
var factory = new DefaultDocumentContextFactory(Dispatcher, new TestDocumentResolver(), _documentVersionCache, LoggerFactory);
var factory = new DefaultDocumentContextFactory(new TestDocumentResolver(), _documentVersionCache, LoggerFactory);

// Act
var documentContext = await factory.TryCreateAsync(uri, DisposalToken);
Expand All @@ -46,7 +46,7 @@ public async Task TryCreateForOpenDocumentAsync_CanNotResolveDocument_ReturnsNul
{
// Arrange
var uri = new Uri("C:/path/to/file.cshtml");
var factory = new DefaultDocumentContextFactory(Dispatcher, new TestDocumentResolver(), _documentVersionCache, LoggerFactory);
var factory = new DefaultDocumentContextFactory(new TestDocumentResolver(), _documentVersionCache, LoggerFactory);

// Act
var documentContext = await factory.TryCreateForOpenDocumentAsync(uri, DisposalToken);
Expand All @@ -62,7 +62,7 @@ public async Task TryCreateForOpenDocumentAsync_CanNotResolveVersion_ReturnsNull
var uri = new Uri("C:/path/to/file.cshtml");
var documentSnapshot = TestDocumentSnapshot.Create(uri.GetAbsoluteOrUNCPath());
var documentResolver = new TestDocumentResolver(documentSnapshot);
var factory = new DefaultDocumentContextFactory(Dispatcher, documentResolver, _documentVersionCache, LoggerFactory);
var factory = new DefaultDocumentContextFactory(documentResolver, _documentVersionCache, LoggerFactory);

// Act
var documentContext = await factory.TryCreateForOpenDocumentAsync(uri, DisposalToken);
Expand All @@ -80,7 +80,7 @@ public async Task TryCreateAsync_ResolvesContent()
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create(string.Empty, documentSnapshot.FilePath));
documentSnapshot.With(codeDocument);
var documentResolver = new TestDocumentResolver(documentSnapshot);
var factory = new DefaultDocumentContextFactory(Dispatcher, documentResolver, _documentVersionCache, LoggerFactory);
var factory = new DefaultDocumentContextFactory(documentResolver, _documentVersionCache, LoggerFactory);

// Act
var documentContext = await factory.TryCreateAsync(uri, DisposalToken);
Expand All @@ -101,7 +101,7 @@ public async Task TryCreateForOpenDocumentAsync_ResolvesContent()
documentSnapshot.With(codeDocument);
var documentResolver = new TestDocumentResolver(documentSnapshot);
await Dispatcher.RunOnDispatcherThreadAsync(() => _documentVersionCache.TrackDocumentVersion(documentSnapshot, version: 1337), DisposalToken);
var factory = new DefaultDocumentContextFactory(Dispatcher, documentResolver, _documentVersionCache, LoggerFactory);
var factory = new DefaultDocumentContextFactory(documentResolver, _documentVersionCache, LoggerFactory);

// Act
var documentContext = await factory.TryCreateForOpenDocumentAsync(uri, DisposalToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public async Task Handle_SearchFound_GenericComponent()
// Arrange
var tagHelperDescriptor1 = CreateRazorComponentTagHelperDescriptor("First", "First.Components", "Component1", typeName: "Component1<TItem>");
var tagHelperDescriptor2 = CreateRazorComponentTagHelperDescriptor("Second", "Second.Components", "Component3", typeName: "Component3<TItem>");
var searchEngine = new DefaultRazorComponentSearchEngine(Dispatcher, s_projectSnapshotManager, LoggerFactory);
var searchEngine = new DefaultRazorComponentSearchEngine(s_projectSnapshotManager, LoggerFactory);

// Act
var documentSnapshot1 = await searchEngine.TryLocateComponentAsync(tagHelperDescriptor1);
Expand All @@ -52,7 +52,7 @@ public async Task Handle_SearchFound()
// Arrange
var tagHelperDescriptor1 = CreateRazorComponentTagHelperDescriptor("First", "First.Components", "Component1");
var tagHelperDescriptor2 = CreateRazorComponentTagHelperDescriptor("Second", "Second.Components", "Component3");
var searchEngine = new DefaultRazorComponentSearchEngine(Dispatcher, s_projectSnapshotManager, LoggerFactory);
var searchEngine = new DefaultRazorComponentSearchEngine(s_projectSnapshotManager, LoggerFactory);

// Act
var documentSnapshot1 = await searchEngine.TryLocateComponentAsync(tagHelperDescriptor1);
Expand All @@ -68,7 +68,7 @@ public async Task Handle_SearchFound_SetNamespace()
{
// Arrange
var tagHelperDescriptor = CreateRazorComponentTagHelperDescriptor("First", "Test", "Component2");
var searchEngine = new DefaultRazorComponentSearchEngine(Dispatcher, s_projectSnapshotManager, LoggerFactory);
var searchEngine = new DefaultRazorComponentSearchEngine(s_projectSnapshotManager, LoggerFactory);

// Act
var documentSnapshot = await searchEngine.TryLocateComponentAsync(tagHelperDescriptor);
Expand All @@ -82,7 +82,7 @@ public async Task Handle_SearchMissing_IncorrectAssembly()
{
// Arrange
var tagHelperDescriptor = CreateRazorComponentTagHelperDescriptor("Third", "First.Components", "Component3");
var searchEngine = new DefaultRazorComponentSearchEngine(Dispatcher, s_projectSnapshotManager, LoggerFactory);
var searchEngine = new DefaultRazorComponentSearchEngine(s_projectSnapshotManager, LoggerFactory);

// Act
var documentSnapshot = await searchEngine.TryLocateComponentAsync(tagHelperDescriptor);
Expand All @@ -96,7 +96,7 @@ public async Task Handle_SearchMissing_IncorrectNamespace()
{
// Arrange
var tagHelperDescriptor = CreateRazorComponentTagHelperDescriptor("First", "First.Components", "Component2");
var searchEngine = new DefaultRazorComponentSearchEngine(Dispatcher, s_projectSnapshotManager, LoggerFactory);
var searchEngine = new DefaultRazorComponentSearchEngine(s_projectSnapshotManager, LoggerFactory);

// Act
var documentSnapshot = await searchEngine.TryLocateComponentAsync(tagHelperDescriptor);
Expand All @@ -110,7 +110,7 @@ public async Task Handle_SearchMissing_IncorrectComponent()
{
// Arrange
var tagHelperDescriptor = CreateRazorComponentTagHelperDescriptor("First", "First.Components", "Component3");
var searchEngine = new DefaultRazorComponentSearchEngine(Dispatcher, s_projectSnapshotManager, LoggerFactory);
var searchEngine = new DefaultRazorComponentSearchEngine(s_projectSnapshotManager, LoggerFactory);

// Act
var documentSnapshot = await searchEngine.TryLocateComponentAsync(tagHelperDescriptor);
Expand All @@ -124,7 +124,7 @@ public async Task Handle_FilePathAndAssemblyNameDifferent()
{
// Arrange
var tagHelperDescriptor = CreateRazorComponentTagHelperDescriptor("AssemblyName", "Test", "Component2");
var searchEngine = new DefaultRazorComponentSearchEngine(Dispatcher, s_projectSnapshotManager, LoggerFactory);
var searchEngine = new DefaultRazorComponentSearchEngine(s_projectSnapshotManager, LoggerFactory);

// Act
var documentSnapshot = await searchEngine.TryLocateComponentAsync(tagHelperDescriptor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ private async Task VerifyCSharpGoToDefinitionAsync(string input, string? filePat
var projectSnapshotManager = Mock.Of<ProjectSnapshotManagerBase>(p => p.GetProjects() == new[] { Mock.Of<IProjectSnapshot>(MockBehavior.Strict) }.ToImmutableArray(), MockBehavior.Strict);
var projectSnapshotManagerAccessor = new TestProjectSnapshotManagerAccessor(projectSnapshotManager);
var projectSnapshotManagerDispatcher = new LSPProjectSnapshotManagerDispatcher(LoggerFactory);
var searchEngine = new DefaultRazorComponentSearchEngine(Dispatcher, projectSnapshotManagerAccessor, LoggerFactory);
var searchEngine = new DefaultRazorComponentSearchEngine(projectSnapshotManagerAccessor, LoggerFactory);

var razorUri = new Uri(razorFilePath);
var documentContext = await DocumentContextFactory.TryCreateForOpenDocumentAsync(razorUri, DisposalToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public async Task Handle_Rename_SingleServer_CSharpEditsAreMapped()
var projectSnapshotManager = Mock.Of<ProjectSnapshotManagerBase>(p => p.GetProjects() == new[] { Mock.Of<IProjectSnapshot>(MockBehavior.Strict) }.ToImmutableArray(), MockBehavior.Strict);
var projectSnapshotManagerAccessor = new TestProjectSnapshotManagerAccessor(projectSnapshotManager);
using var projectSnapshotManagerDispatcher = new LSPProjectSnapshotManagerDispatcher(LoggerFactory);
var searchEngine = new DefaultRazorComponentSearchEngine(Dispatcher, projectSnapshotManagerAccessor, LoggerFactory);
var searchEngine = new DefaultRazorComponentSearchEngine(projectSnapshotManagerAccessor, LoggerFactory);

var endpoint = new RenameEndpoint(
projectSnapshotManagerDispatcher,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -699,7 +699,7 @@ private RenameEndpoint CreateEndpoint(LanguageServerFeatureOptions languageServe

var projectSnapshotManagerDispatcher = new LSPProjectSnapshotManagerDispatcher(LoggerFactory);

var searchEngine = new DefaultRazorComponentSearchEngine(Dispatcher, projectSnapshotManagerAccessor, LoggerFactory);
var searchEngine = new DefaultRazorComponentSearchEngine(projectSnapshotManagerAccessor, LoggerFactory);
languageServerFeatureOptions ??= Mock.Of<LanguageServerFeatureOptions>(
options => options.SupportsFileManipulation == true && options.SingleServerSupport == false && options.ReturnCodeActionAndRenamePathsWithPrefixedSlash == false, MockBehavior.Strict);

Expand Down