Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace IProjectCollectionResolver with ISolutionQueryOperations #10943

Merged
merged 10 commits into from
Oct 2, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.GoToDefinition;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.VisualStudio.LanguageServer.Protocol;
Expand All @@ -23,6 +24,7 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Definition;
internal sealed class DefinitionEndpoint(
IRazorComponentDefinitionService componentDefinitionService,
IDocumentMappingService documentMappingService,
IProjectQueryService projectQueryService,
LanguageServerFeatureOptions languageServerFeatureOptions,
IClientConnection clientConnection,
ILoggerFactory loggerFactory)
Expand All @@ -34,6 +36,7 @@ internal sealed class DefinitionEndpoint(
{
private readonly IRazorComponentDefinitionService _componentDefinitionService = componentDefinitionService;
private readonly IDocumentMappingService _documentMappingService = documentMappingService;
private readonly IProjectQueryService _projectQueryService = projectQueryService;

protected override bool PreferCSharpOverHtmlIfPossible => true;

Expand Down Expand Up @@ -62,7 +65,7 @@ public void ApplyCapabilities(VSInternalServerCapabilities serverCapabilities, V

// If single server support is on, then we ignore attributes, as they are better handled by delegating to Roslyn
return await _componentDefinitionService
.GetDefinitionAsync(documentContext.Snapshot, positionInfo, ignoreAttributes: SingleServerSupport, cancellationToken)
.GetDefinitionAsync(documentContext.Snapshot, positionInfo, _projectQueryService, ignoreAttributes: SingleServerSupport, cancellationToken)
.ConfigureAwait(false);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ public static void AddDocumentManagementServices(this IServiceCollection service
// Add project snapshot manager
services.AddSingleton<IProjectEngineFactoryProvider, LspProjectEngineFactoryProvider>();
services.AddSingleton<IProjectSnapshotManager, LspProjectSnapshotManager>();
services.AddSingleton<IProjectCollectionResolver>(sp => (LspProjectSnapshotManager)sp.GetRequiredService<IProjectSnapshotManager>());
services.AddSingleton<IProjectQueryService, LspProjectQueryService>();
}

public static void AddHandlerWithCapabilities<T>(this IServiceCollection services)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;

namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem;

internal sealed class LspProjectQueryService(IProjectSnapshotManager projectSnapshotManager) : IProjectQueryService
{
private readonly IProjectSnapshotManager _projectSnapshotManager = projectSnapshotManager;

public IEnumerable<IProjectSnapshot> GetProjects()
{
return _projectSnapshotManager.GetProjects();
}

public ImmutableArray<IProjectSnapshot> FindProjects(string documentFilePath)
{
using var results = new PooledArrayBuilder<IProjectSnapshot>();

foreach (var project in _projectSnapshotManager.GetProjects())
{
if (!project.TryGetDocument(documentFilePath, out _))
{
results.Add(project);
}
}

return results.DrainToImmutable();
}
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,19 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. 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.ProjectEngineHost;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Workspaces;

namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem;

internal class LspProjectSnapshotManager(
IProjectEngineFactoryProvider projectEngineFactoryProvider,
ILoggerFactory loggerFactory)
: ProjectSnapshotManager(projectEngineFactoryProvider, loggerFactory, initializer: AddMiscFilesProject), IProjectCollectionResolver
: ProjectSnapshotManager(projectEngineFactoryProvider, loggerFactory, initializer: AddMiscFilesProject)
{
private static void AddMiscFilesProject(Updater updater)
{
updater.ProjectAdded(MiscFilesHostProject.Instance);
}

public IEnumerable<IProjectSnapshot> EnumerateProjects(IDocumentSnapshot snapshot)
{
return GetProjects();
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.LanguageServer.EndpointContracts;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.AspNetCore.Razor.Threading;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
Expand All @@ -32,6 +22,7 @@ internal sealed class RenameEndpoint(
LanguageServerFeatureOptions languageServerFeatureOptions,
IDocumentMappingService documentMappingService,
IEditMappingService editMappingService,
IProjectQueryService projectQueryService,
IClientConnection clientConnection,
ILoggerFactory loggerFactory)
: AbstractRazorDelegatingEndpoint<RenameParams, WorkspaceEdit?>(
Expand All @@ -43,6 +34,7 @@ internal sealed class RenameEndpoint(
private readonly IRenameService _renameService = renameService;
private readonly LanguageServerFeatureOptions _languageServerFeatureOptions = languageServerFeatureOptions;
private readonly IEditMappingService _editMappingService = editMappingService;
private readonly IProjectQueryService _projectQueryService = projectQueryService;

public void ApplyCapabilities(VSInternalServerCapabilities serverCapabilities, VSInternalClientCapabilities clientCapabilities)
{
Expand All @@ -64,7 +56,7 @@ public void ApplyCapabilities(VSInternalServerCapabilities serverCapabilities, V
return SpecializedTasks.Null<WorkspaceEdit>();
}

return _renameService.TryGetRazorRenameEditsAsync(documentContext, positionInfo, request.NewName, cancellationToken);
return _renameService.TryGetRazorRenameEditsAsync(documentContext, positionInfo, request.NewName, _projectQueryService, cancellationToken);
}

protected override bool IsSupported()
Expand Down

This file was deleted.

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

using System.Buffers;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.AspNetCore.Razor.Telemetry;
using Microsoft.CodeAnalysis.Razor;

namespace Microsoft.CodeAnalysis;

internal static class ProjectExtensions
{
private const string GetTagHelpersEventName = "taghelperresolver/gettaghelpers";
private const string PropertySuffix = ".elapsedtimems";

/// <summary>
/// Gets the available <see cref="TagHelperDescriptor">tag helpers</see> from the specified
/// <see cref="Project"/> using the given <see cref="RazorProjectEngine"/>.
/// </summary>
public static ValueTask<ImmutableArray<TagHelperDescriptor>> GetTagHelpersAsync(
this Project project,
RazorProjectEngine projectEngine,
CancellationToken cancellationToken)
=> project.GetTagHelpersAsync(projectEngine, NoOpTelemetryReporter.Instance, cancellationToken);

/// <summary>
/// Gets the available <see cref="TagHelperDescriptor">tag helpers</see> from the specified
/// <see cref="Project"/> using the given <see cref="RazorProjectEngine"/>.
/// </summary>
/// <remarks>
/// A telemetry event will be reported to <paramref name="telemetryReporter"/>.
/// </remarks>
public static async ValueTask<ImmutableArray<TagHelperDescriptor>> GetTagHelpersAsync(
this Project project,
RazorProjectEngine projectEngine,
ITelemetryReporter telemetryReporter,
CancellationToken cancellationToken)
{
var providers = GetTagHelperDescriptorProviders(projectEngine);

if (providers is [])
{
return [];
}

var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
if (compilation is null || !CompilationTagHelperFeature.IsValidCompilation(compilation))
{
return [];
}

using var pooledHashSet = HashSetPool<TagHelperDescriptor>.GetPooledObject(out var results);
using var pooledWatch = StopwatchPool.GetPooledObject(out var watch);
using var pooledSpan = ArrayPool<Property>.Shared.GetPooledArraySpan(minimumLength: providers.Length, out var properties);

var context = new TagHelperDescriptorProviderContext(compilation, results)
{
ExcludeHidden = true,
IncludeDocumentation = true
};

var writeProperties = properties;

foreach (var provider in providers)
{
watch.Restart();
provider.Execute(context);
watch.Stop();

writeProperties[0] = new(provider.GetType().Name + PropertySuffix, watch.ElapsedMilliseconds);
writeProperties = writeProperties[1..];
}

telemetryReporter.ReportEvent(GetTagHelpersEventName, Severity.Normal, properties);

return [.. results];
}

private static ImmutableArray<ITagHelperDescriptorProvider> GetTagHelperDescriptorProviders(RazorProjectEngine projectEngine)
{
using var result = new PooledArrayBuilder<ITagHelperDescriptorProvider>();

foreach (var feature in projectEngine.Engine.Features)
{
if (feature is ITagHelperDescriptorProvider provider)
{
result.Add(provider);
}
}

return result.DrainToImmutableOrderedBy(static x => x.Order);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
using Microsoft.AspNetCore.Razor.ProjectEngineHost;
using Microsoft.AspNetCore.Razor.ProjectSystem;
using Microsoft.AspNetCore.Razor.Serialization;
using Microsoft.AspNetCore.Razor.Telemetry;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;
Expand Down Expand Up @@ -86,8 +85,7 @@ static RazorProjectInfoFactory()
fileSystem,
configure: defaultConfigure);

var resolver = new CompilationTagHelperResolver(NoOpTelemetryReporter.Instance);
var tagHelpers = await resolver.GetTagHelpersAsync(project, engine, cancellationToken).ConfigureAwait(false);
var tagHelpers = await project.GetTagHelpersAsync(engine, cancellationToken).ConfigureAwait(false);

var projectWorkspaceState = ProjectWorkspaceState.Create(tagHelpers, csharpLanguageVersion);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ internal abstract class AbstractRazorComponentDefinitionService(
private readonly IDocumentMappingService _documentMappingService = documentMappingService;
private readonly ILogger _logger = logger;

public async Task<LspLocation?> GetDefinitionAsync(IDocumentSnapshot documentSnapshot, DocumentPositionInfo positionInfo, bool ignoreAttributes, CancellationToken cancellationToken)
public async Task<LspLocation?> GetDefinitionAsync(
IDocumentSnapshot documentSnapshot,
DocumentPositionInfo positionInfo,
IProjectQueryService projectQueryService,
bool ignoreAttributes,
CancellationToken cancellationToken)
{
// If we're in C# then there is no point checking for a component tag, because there won't be one
if (positionInfo.LanguageKind == RazorLanguageKind.CSharp)
Expand All @@ -47,7 +52,7 @@ internal abstract class AbstractRazorComponentDefinitionService(
return null;
}

var componentDocument = await _componentSearchEngine.TryLocateComponentAsync(documentSnapshot, boundTagHelper).ConfigureAwait(false);
var componentDocument = await _componentSearchEngine.TryLocateComponentAsync(boundTagHelper, projectQueryService).ConfigureAwait(false);
if (componentDocument is null)
{
_logger.LogInformation($"Could not locate component document.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,10 @@ namespace Microsoft.CodeAnalysis.Razor.GoToDefinition;
/// </summary>
internal interface IRazorComponentDefinitionService
{
Task<LspLocation?> GetDefinitionAsync(IDocumentSnapshot documentSnapshot, DocumentPositionInfo positionInfo, bool ignoreAttributes, CancellationToken cancellationToken);
Task<LspLocation?> GetDefinitionAsync(
IDocumentSnapshot documentSnapshot,
DocumentPositionInfo positionInfo,
IProjectQueryService projectQueryService,
bool ignoreAttributes,
CancellationToken cancellationToken);
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces;

internal interface IRazorComponentSearchEngine
{
Task<IDocumentSnapshot?> TryLocateComponentAsync(IDocumentSnapshot contextSnapshot, TagHelperDescriptor tagHelper);
Task<IDocumentSnapshot?> TryLocateComponentAsync(TagHelperDescriptor tagHelper, IProjectQueryService projectQueryService);
}
Loading
Loading