Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -87,7 +87,7 @@ await projectManager.UpdateAsync(

private class NoopClientNotifierService : IClientConnection, IOnInitialized
{
public Task OnInitializedAsync(VSInternalClientCapabilities clientCapabilities, CancellationToken cancellationToken)
public Task OnInitializedAsync(ILspServices services, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.CommonLanguageServerProtocol.Framework;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using StreamJsonRpc;

Expand Down Expand Up @@ -47,7 +48,7 @@ public async Task SendNotificationAsync(string method, CancellationToken cancell
/// <summary>
/// Fires when the language server is set to "Started".
/// </summary>
public Task OnInitializedAsync(VSInternalClientCapabilities clientCapabilities, CancellationToken cancellationToken)
public Task OnInitializedAsync(ILspServices services, CancellationToken cancellationToken)
{
_initializedCompletionSource.TrySetResult(true);
return Task.CompletedTask;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,7 @@ public async override Task<CodeAction> ResolveAsync(
throw new ArgumentNullException(nameof(codeAction));
}

var documentContext = await _documentContextFactory.TryCreateForOpenDocumentAsync(csharpParams.RazorFileIdentifier, cancellationToken).ConfigureAwait(false);
if (documentContext is null)
if (!_documentContextFactory.TryCreateForOpenDocument(csharpParams.RazorFileIdentifier, out var documentContext))
{
return codeAction;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@ public async override Task<CodeAction> ResolveAsync(

cancellationToken.ThrowIfCancellationRequested();

var documentContext = await _documentContextFactory.TryCreateForOpenDocumentAsync(csharpParams.RazorFileIdentifier, cancellationToken).ConfigureAwait(false);
if (documentContext is null)
if (!_documentContextFactory.TryCreateForOpenDocument(csharpParams.RazorFileIdentifier, out var documentContext))
{
return codeAction;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@ public async override Task<CodeAction> ResolveAsync(
throw new ArgumentNullException(nameof(codeAction));
}

var documentContext = await _documentContextFactory.TryCreateForOpenDocumentAsync(resolveParams.RazorFileIdentifier, cancellationToken).ConfigureAwait(false);
if (documentContext is null)
if (!_documentContextFactory.TryCreateForOpenDocument(resolveParams.RazorFileIdentifier, out var documentContext))
{
return codeAction;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ public AddUsingsCodeActionResolver(IDocumentContextFactory documentContextFactor
return null;
}

var documentContext = await _documentContextFactory.TryCreateAsync(actionParams.Uri, cancellationToken).ConfigureAwait(false);
if (documentContext is null)
if (!_documentContextFactory.TryCreate(actionParams.Uri, out var documentContext))
{
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ public CreateComponentCodeActionResolver(IDocumentContextFactory documentContext
return null;
}

var documentContext = await _documentContextFactory.TryCreateAsync(actionParams.Uri, cancellationToken).ConfigureAwait(false);
if (documentContext is null)
if (!_documentContextFactory.TryCreate(actionParams.Uri, out var documentContext))
{
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,7 @@ public ExtractToCodeBehindCodeActionResolver(

var path = FilePathNormalizer.Normalize(actionParams.Uri.GetAbsoluteOrUNCPath());

var documentContext = await _documentContextFactory.TryCreateAsync(actionParams.Uri, cancellationToken).ConfigureAwait(false);
if (documentContext is null)
if (!_documentContextFactory.TryCreate(actionParams.Uri, out var documentContext))
{
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,7 @@ public GenerateMethodCodeActionResolver(
return null;
}

var documentContext = await _documentContextFactory.TryCreateForOpenDocumentAsync(actionParams.Uri, cancellationToken).ConfigureAwait(false);
if (documentContext is null)
if (!_documentContextFactory.TryCreateForOpenDocument(actionParams.Uri, out var documentContext))
{
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,7 @@ private async Task<VSInternalCompletionItem> PostProcessCompletionItemAsync(
}

var identifier = context.OriginalRequestParams.Identifier.TextDocumentIdentifier;
var documentContext = await _documentContextFactory.TryCreateForOpenDocumentAsync(identifier, cancellationToken).ConfigureAwait(false);
if (documentContext is null)
if (!_documentContextFactory.TryCreateForOpenDocument(identifier, out var documentContext))
{
return resolvedCompletionItem;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,11 +183,7 @@ .. csharpDiagnostics ?? []
delegatedResponse.Value.TryGetFirst(out var fullDiagnostics) &&
fullDiagnostics.Items is not null)
{
var documentContext = await _documentContextFactory.Value
.TryCreateAsync(delegatedParams.TextDocument.Uri, projectContext: null, token)
.ConfigureAwait(false);

if (documentContext is not null)
if (_documentContextFactory.Value.TryCreate(delegatedParams.TextDocument.Uri, projectContext: null, out var documentContext))
{
return await _translateDiagnosticsService.Value
.TranslateAsync(RazorLanguageKind.CSharp, fullDiagnostics.Items, documentContext, token)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem;
Expand All @@ -27,52 +28,64 @@ internal sealed class DocumentContextFactory(
private readonly IDocumentVersionCache _documentVersionCache = documentVersionCache;
private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<DocumentContextFactory>();

public async Task<DocumentContext?> TryCreateAsync(Uri documentUri, VSProjectContext? projectContext, bool versioned, CancellationToken cancellationToken)
public bool TryCreate(
Uri documentUri,
VSProjectContext? projectContext,
bool versioned,
[NotNullWhen(true)] out DocumentContext? context)
{
var filePath = documentUri.GetAbsoluteOrUNCPath();
var documentAndVersion = await TryGetDocumentAndVersionAsync(filePath, projectContext, versioned, cancellationToken).ConfigureAwait(false);

if (documentAndVersion is null)
if (!TryGetDocumentAndVersion(filePath, projectContext, versioned, out var documentAndVersion))
{
// Stale request or misbehaving client, see above comment.
return null;
context = null;
return false;
}

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;
context = null;
return false;
}

if (versioned)
{
// 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;
context = null;
return false;
}

return new VersionedDocumentContext(documentUri, documentSnapshot, projectContext, version.Value);
context = new VersionedDocumentContext(documentUri, documentSnapshot, projectContext, version.Value);
return true;
}

return new DocumentContext(documentUri, documentSnapshot, projectContext);
context = new DocumentContext(documentUri, documentSnapshot, projectContext);
return true;
}

private async Task<DocumentSnapshotAndVersion?> TryGetDocumentAndVersionAsync(string filePath, VSProjectContext? projectContext, bool versioned, CancellationToken cancellationToken)
private bool TryGetDocumentAndVersion(
string filePath,
VSProjectContext? projectContext,
bool versioned,
[NotNullWhen(true)] out DocumentSnapshotAndVersion? documentAndVersion)
{
var documentSnapshot = await TryResolveDocumentAsync(filePath, projectContext, cancellationToken).ConfigureAwait(false);

if (documentSnapshot is not null)
if (TryResolveDocument(filePath, projectContext, out var documentSnapshot))
{
if (!versioned)
{
return new DocumentSnapshotAndVersion(documentSnapshot, Version: null);
documentAndVersion = new DocumentSnapshotAndVersion(documentSnapshot, Version: null);
return true;
}

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

_logger.LogWarning($"Tried to create context for document {filePath} and project {projectContext?.Id} and a document was found, but version didn't match.");
Expand All @@ -85,36 +98,41 @@ internal sealed class DocumentContextFactory(
// 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} and project {projectContext?.Id} which was not found.");
return null;
documentAndVersion = null;
return false;
}

private async Task<IDocumentSnapshot?> TryResolveDocumentAsync(string filePath, VSProjectContext? projectContext, CancellationToken cancellationToken)
private bool TryResolveDocument(
string filePath,
VSProjectContext? projectContext,
[NotNullWhen(true)] out IDocumentSnapshot? documentSnapshot)
{
if (projectContext is null)
{
return await _snapshotResolver
.ResolveDocumentInAnyProjectAsync(filePath, cancellationToken)
.ConfigureAwait(false);
return _snapshotResolver.TryResolveDocumentInAnyProject(filePath, out documentSnapshot);
}

if (_projectManager.TryGetLoadedProject(projectContext.ToProjectKey(), out var project) &&
project.GetDocument(filePath) is { } document)
{
return document;
documentSnapshot = document;
return true;
}

// Couldn't find the document in a real project. Maybe the language server doesn't yet know about the project
// that the IDE is asking us about. In that case, we might have the document in our misc files project, and we'll
// move it to the real project when/if we find out about it.
var miscellaneousProject = await _snapshotResolver.GetMiscellaneousProjectAsync(cancellationToken).ConfigureAwait(false);
var miscellaneousProject = _snapshotResolver.GetMiscellaneousProject();
var normalizedDocumentPath = FilePathNormalizer.Normalize(filePath);
if (miscellaneousProject.GetDocument(normalizedDocumentPath) is { } miscDocument)
{
_logger.LogDebug($"Found document {filePath} in the misc files project, but was asked for project context {projectContext.Id}");
return miscDocument;
documentSnapshot = miscDocument;
return true;
}

return null;
documentSnapshot = null;
return false;
}

private record DocumentSnapshotAndVersion(IDocumentSnapshot Snapshot, int? Version);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,7 @@ protected override IRazorPresentationParams CreateRazorRequestParameters(UriPres
{
Logger.LogInformation($"Trying to find document info for dropped uri {uri}.");

var documentContext = await _documentContextFactory.TryCreateAsync(uri, cancellationToken).ConfigureAwait(false);
if (documentContext is null)
if (!_documentContextFactory.TryCreate(uri, out var documentContext))
{
Logger.LogInformation($"Failed to find document for component {uri}.");
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Extensions;

internal static class IServiceCollectionExtensions
{
public static void AddLifeCycleServices(this IServiceCollection services, RazorLanguageServer razorLanguageServer, ClientConnection serverManager, ILspServerActivationTracker? lspServerActivationTracker)
public static void AddLifeCycleServices(this IServiceCollection services, RazorLanguageServer razorLanguageServer, ClientConnection clientConnection, ILspServerActivationTracker? lspServerActivationTracker)
{
services.AddHandler<RazorInitializeEndpoint>();
services.AddHandler<RazorInitializedEndpoint>();
Expand All @@ -51,7 +51,7 @@ public static void AddLifeCycleServices(this IServiceCollection services, RazorL

services.AddSingleton<ICapabilitiesProvider, RazorLanguageServerCapability>();

services.AddSingleton<IOnInitialized>(serverManager);
services.AddSingleton<IOnInitialized>(clientConnection);
}

public static void AddFormattingServices(this IServiceCollection services)
Expand Down Expand Up @@ -207,10 +207,12 @@ public static void AddDocumentManagementServices(this IServiceCollection service

services.AddSingleton<RemoteTextLoaderFactory, DefaultRemoteTextLoaderFactory>();
services.AddSingleton<ISnapshotResolver, SnapshotResolver>();
services.AddSingleton<IOnInitialized>(sp => (SnapshotResolver)sp.GetRequiredService<ISnapshotResolver>());
services.AddSingleton<IRazorProjectService, RazorProjectService>();
services.AddSingleton<IRazorStartupService, OpenDocumentGenerator>();
services.AddSingleton<IRazorDocumentMappingService, RazorDocumentMappingService>();
services.AddSingleton<RazorFileChangeDetectorManager>();
services.AddSingleton<IOnInitialized>(sp => sp.GetRequiredService<RazorFileChangeDetectorManager>());

if (featureOptions.UseProjectConfigurationEndpoint)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.

using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.VisualStudio.LanguageServer.Protocol;

namespace Microsoft.AspNetCore.Razor.LanguageServer.Hover;

internal sealed partial class HoverService
{
internal TestAccessor GetTestAccessor() => new(this);

internal sealed class TestAccessor(HoverService instance)
{
public Task<VSInternalHover?> GetHoverInfoAsync(
string documentFilePath,
RazorCodeDocument codeDocument,
SourceLocation location,
VSInternalClientCapabilities clientCapabilities,
CancellationToken cancellationToken)
=> instance.GetHoverInfoAsync(documentFilePath, codeDocument, location, clientCapabilities, cancellationToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

namespace Microsoft.AspNetCore.Razor.LanguageServer.Hover;

internal sealed class HoverService(
internal sealed partial class HoverService(
LSPTagHelperTooltipFactory lspTagHelperTooltipFactory,
VSLSPTagHelperTooltipFactory vsLspTagHelperTooltipFactory,
IRazorDocumentMappingService mappingService,
Expand Down Expand Up @@ -94,15 +94,13 @@ internal sealed class HoverService(
return response;
}

public TestAccessor GetTestAccessor() => new(this);

public async Task<VSInternalHover?> GetHoverInfoAsync(string documentFilePath, RazorCodeDocument codeDocument, SourceLocation location, VSInternalClientCapabilities clientCapabilities, CancellationToken cancellationToken)
private async Task<VSInternalHover?> GetHoverInfoAsync(
string documentFilePath,
RazorCodeDocument codeDocument,
SourceLocation location,
VSInternalClientCapabilities clientCapabilities,
CancellationToken cancellationToken)
{
if (codeDocument is null)
{
throw new ArgumentNullException(nameof(codeDocument));
}

var syntaxTree = codeDocument.GetSyntaxTree();

var owner = syntaxTree.Root.FindInnermostNode(location.AbsoluteIndex);
Expand Down Expand Up @@ -350,10 +348,4 @@ private static VisualStudioMarkupKind GetHoverContentFormat(ClientCapabilities c
var hoverKind = hoverContentFormat?.Contains(VisualStudioMarkupKind.Markdown) == true ? VisualStudioMarkupKind.Markdown : VisualStudioMarkupKind.PlainText;
return hoverKind;
}

public class TestAccessor(HoverService service)
{
public Task<VSInternalHover?> GetHoverInfoAsync(string documentFilePath, RazorCodeDocument codeDocument, SourceLocation location, VSInternalClientCapabilities clientCapabilities, CancellationToken cancellationToken)
=> service.GetHoverInfoAsync(documentFilePath, codeDocument, location, clientCapabilities, cancellationToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@

using System.Threading;
using System.Threading.Tasks;
using Microsoft.CommonLanguageServerProtocol.Framework;
using Microsoft.VisualStudio.LanguageServer.Protocol;

namespace Microsoft.AspNetCore.Razor.LanguageServer;

internal interface IOnInitialized
{
Task OnInitializedAsync(VSInternalClientCapabilities clientCapabilities, CancellationToken cancellationToken);
Task OnInitializedAsync(ILspServices services, CancellationToken cancellationToken);
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,7 @@ public void ApplyCapabilities(VSInternalServerCapabilities serverCapabilities, V
continue;
}

var documentContext = await _documentContextFactory.TryCreateForOpenDocumentAsync(mapping.TextDocument.Uri, cancellationToken).ConfigureAwait(false);
if (documentContext is null)
if (!_documentContextFactory.TryCreateForOpenDocument(mapping.TextDocument.Uri, out var documentContext))
{
continue;
}
Expand Down Expand Up @@ -356,8 +355,7 @@ private async Task<Location[][]> GetCSharpFocusLocationsAsync(Location[][] focus
continue;
}

var documentContext = await _documentContextFactory.TryCreateForOpenDocumentAsync(potentialLocation.Uri, cancellationToken).ConfigureAwait(false);
if (documentContext is null)
if (!_documentContextFactory.TryCreateForOpenDocument(potentialLocation.Uri, out var documentContext))
{
continue;
}
Expand Down
Loading