Skip to content

Commit

Permalink
Merge Cohost feature branch into main (#9786)
Browse files Browse the repository at this point in the history
Part of #9519

Now that the initial cohost PR has been merged, we don't need to use the
feature branch any more. That means this PR, which is big, but all of
the code in it has been reviewed, in
#9761,
#9766 and
#9767.
  • Loading branch information
davidwengier committed Jan 5, 2024
2 parents dbc2fa8 + 1f6d05d commit 55551d6
Show file tree
Hide file tree
Showing 22 changed files with 455 additions and 112 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ public class RazorSemanticTokensBenchmark : RazorLanguageServerBenchmarkBase

private ProjectSnapshotManagerDispatcher ProjectSnapshotManagerDispatcher { get; set; }

private RazorSemanticTokensLegend SemanticTokensLegend { get; set; }

private string PagesDirectory { get; set; }

private string ProjectFilePath { get; set; }
Expand Down Expand Up @@ -81,8 +79,6 @@ public async Task InitializeRazorSemanticAsync()
Character = text.Lines.Last().Span.Length - 1
}
};

SemanticTokensLegend = new RazorSemanticTokensLegend(new VSInternalClientCapabilities() { SupportsVisualStudioExtensions = true });
}

[Benchmark(Description = "Razor Semantic Tokens Range Handling")]
Expand All @@ -94,11 +90,12 @@ public async Task RazorSemanticTokensRangeAsync()
};
var cancellationToken = CancellationToken.None;
var documentVersion = 1;
var correlationId = Guid.Empty;

await UpdateDocumentAsync(documentVersion, DocumentSnapshot, cancellationToken).ConfigureAwait(false);

var clientConnection = RazorLanguageServer.GetRequiredService<IClientConnection>();
await RazorSemanticTokenService.GetSemanticTokensAsync(
textDocumentIdentifier, Range, DocumentContext, SemanticTokensLegend, correlationId, cancellationToken).ConfigureAwait(false);
clientConnection, textDocumentIdentifier, Range, DocumentContext, colorBackground: false, cancellationToken).ConfigureAwait(false);
}

private async Task UpdateDocumentAsync(int newVersion, IDocumentSnapshot documentSnapshot, CancellationToken cancellationToken)
Expand All @@ -124,28 +121,29 @@ private void EnsureServicesInitialized()
{
var languageServer = RazorLanguageServer.GetInnerLanguageServerForTesting();
RazorSemanticTokenService = languageServer.GetRequiredService<IRazorSemanticTokensInfoService>();
RazorSemanticTokenService.ApplyCapabilities(new(), new VSInternalClientCapabilities { SupportsVisualStudioExtensions = true });
VersionCache = languageServer.GetRequiredService<IDocumentVersionCache>();
ProjectSnapshotManagerDispatcher = languageServer.GetRequiredService<ProjectSnapshotManagerDispatcher>();
}

internal class TestRazorSemanticTokensInfoService : RazorSemanticTokensInfoService
{
public TestRazorSemanticTokensInfoService(
IClientConnection clientConnection,
LanguageServerFeatureOptions languageServerFeatureOptions,
IRazorDocumentMappingService documentMappingService,
RazorLSPOptionsMonitor razorLSPOptionsMonitor,
IRazorLoggerFactory loggerFactory)
: base(clientConnection, documentMappingService, razorLSPOptionsMonitor, languageServerFeatureOptions, loggerFactory)
: base(documentMappingService, languageServerFeatureOptions, loggerFactory, telemetryReporter: null)
{
}

// We can't get C# responses without significant amounts of extra work, so let's just shim it for now, any non-Null result is fine.
internal override Task<ImmutableArray<SemanticRange>?> GetCSharpSemanticRangesAsync(
IClientConnection clientConnection,
RazorCodeDocument codeDocument,
TextDocumentIdentifier textDocumentIdentifier,
Range razorRange,
RazorSemanticTokensLegend razorSemanticTokensLegend,
bool colorBackground,
long documentVersion,
Guid correlationId,
CancellationToken cancellationToken,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ public async Task InitializeRazorSemanticAsync()
var documentSnapshot = GetDocumentSnapshot(ProjectFilePath, filePath, TargetPath);
var version = 1;
DocumentContext = new VersionedDocumentContext(documentUri, documentSnapshot, projectContext: null, version);
SemanticTokensRangeEndpoint = new SemanticTokensRangeEndpoint(loggerFactory, telemetryReporter: null);

var razorOptionsMonitor = RazorLanguageServer.GetRequiredService<RazorLSPOptionsMonitor>();
var clientConnection = RazorLanguageServer.GetRequiredService<IClientConnection>();
SemanticTokensRangeEndpoint = new SemanticTokensRangeEndpoint(RazorSemanticTokenService, razorOptionsMonitor, clientConnection);
SemanticTokensRangeEndpoint.ApplyCapabilities(new(), new VSInternalClientCapabilities() { SupportsVisualStudioExtensions = true });

var text = await DocumentContext.GetSourceTextAsync(CancellationToken.None).ConfigureAwait(false);
Expand Down Expand Up @@ -150,21 +153,21 @@ private void EnsureServicesInitialized()
internal class TestCustomizableRazorSemanticTokensInfoService : RazorSemanticTokensInfoService
{
public TestCustomizableRazorSemanticTokensInfoService(
IClientConnection clientConnection,
LanguageServerFeatureOptions languageServerFeatureOptions,
IRazorDocumentMappingService documentMappingService,
RazorLSPOptionsMonitor razorLSPOptionsMonitor,
IRazorLoggerFactory loggerFactory)
: base(clientConnection, documentMappingService, razorLSPOptionsMonitor, languageServerFeatureOptions, loggerFactory)
: base(documentMappingService, languageServerFeatureOptions, loggerFactory, telemetryReporter: null)
{
}

// We can't get C# responses without significant amounts of extra work, so let's just shim it for now, any non-Null result is fine.
internal override Task<ImmutableArray<SemanticRange>?> GetCSharpSemanticRangesAsync(
IClientConnection clientConnection,
RazorCodeDocument codeDocument,
TextDocumentIdentifier textDocumentIdentifier,
Range razorRange,
RazorSemanticTokensLegend razorSemanticTokensLegend,
bool colorBackground,
long documentVersion,
Guid correlationId,
CancellationToken cancellationToken,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public async Task InitializeRazorSemanticAsync()
DocumentContext = new VersionedDocumentContext(documentUri, documentSnapshot, projectContext: null, version: 1);

SemanticTokensLegend = new RazorSemanticTokensLegend(new VSInternalClientCapabilities() { SupportsVisualStudioExtensions = true });
RazorSemanticTokenService.ApplyCapabilities(new(), new VSInternalClientCapabilities() { SupportsVisualStudioExtensions = true });

var text = await DocumentSnapshot.GetTextAsync().ConfigureAwait(false);
Range = new Range
Expand Down Expand Up @@ -85,13 +86,14 @@ public async Task RazorSemanticTokensRangeScrollingAsync()
Uri = DocumentUri
};
var cancellationToken = CancellationToken.None;
var correlationId = Guid.Empty;
var documentVersion = 1;

await UpdateDocumentAsync(documentVersion, DocumentSnapshot).ConfigureAwait(false);

var documentLineCount = Range.End.Line;

var clientConnection = RazorLanguageServer.GetRequiredService<IClientConnection>();

var lineCount = 0;
while (lineCount != documentLineCount)
{
Expand All @@ -102,11 +104,11 @@ public async Task RazorSemanticTokensRangeScrollingAsync()
End = new Position(newLineCount, 0)
};
await RazorSemanticTokenService!.GetSemanticTokensAsync(
clientConnection,
textDocumentIdentifier,
range,
DocumentContext,
SemanticTokensLegend,
correlationId,
colorBackground: false,
cancellationToken);

lineCount = newLineCount;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ internal class ConfigurableLanguageServerFeatureOptions : LanguageServerFeatureO
private readonly bool? _includeProjectKeyInGeneratedFilePath;
private readonly bool? _monitorWorkspaceFolderForConfigurationFiles;
private readonly bool? _useRazorCohostServer;
private readonly bool? _disableRazorLanguageServer;

public override bool SupportsFileManipulation => _supportsFileManipulation ?? _defaults.SupportsFileManipulation;
public override string ProjectConfigurationFileName => _projectConfigurationFileName ?? _defaults.ProjectConfigurationFileName;
Expand All @@ -39,6 +40,7 @@ internal class ConfigurableLanguageServerFeatureOptions : LanguageServerFeatureO
public override bool IncludeProjectKeyInGeneratedFilePath => _includeProjectKeyInGeneratedFilePath ?? _defaults.IncludeProjectKeyInGeneratedFilePath;
public override bool MonitorWorkspaceFolderForConfigurationFiles => _monitorWorkspaceFolderForConfigurationFiles ?? _defaults.MonitorWorkspaceFolderForConfigurationFiles;
public override bool UseRazorCohostServer => _useRazorCohostServer ?? _defaults.UseRazorCohostServer;
public override bool DisableRazorLanguageServer => _disableRazorLanguageServer ?? _defaults.DisableRazorLanguageServer;

public ConfigurableLanguageServerFeatureOptions(string[] args)
{
Expand All @@ -63,6 +65,7 @@ public ConfigurableLanguageServerFeatureOptions(string[] args)
TryProcessBoolOption(nameof(IncludeProjectKeyInGeneratedFilePath), ref _includeProjectKeyInGeneratedFilePath, option, args, i);
TryProcessBoolOption(nameof(MonitorWorkspaceFolderForConfigurationFiles), ref _monitorWorkspaceFolderForConfigurationFiles, option, args, i);
TryProcessBoolOption(nameof(UseRazorCohostServer), ref _useRazorCohostServer, option, args, i);
TryProcessBoolOption(nameof(DisableRazorLanguageServer), ref _disableRazorLanguageServer, option, args, i);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,6 @@ public override bool ReturnCodeActionAndRenamePathsWithPrefixedSlash
public override bool MonitorWorkspaceFolderForConfigurationFiles => true;

public override bool UseRazorCohostServer => false;

public override bool DisableRazorLanguageServer => false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -120,16 +120,19 @@ public static void AddHoverServices(this IServiceCollection services)
services.AddSingleton<IHoverInfoService, HoverInfoService>();
}

public static void AddSemanticTokensServices(this IServiceCollection services)
public static void AddSemanticTokensServices(this IServiceCollection services, LanguageServerFeatureOptions featureOptions)
{
services.AddHandlerWithCapabilities<SemanticTokensRangeEndpoint>();
if (!featureOptions.UseRazorCohostServer)
{
services.AddHandlerWithCapabilities<SemanticTokensRangeEndpoint>();
// Ensure that we don't add the default service if something else has added one.
services.TryAddSingleton<IRazorSemanticTokensInfoService, RazorSemanticTokensInfoService>();
}

services.AddHandler<RazorSemanticTokensRefreshEndpoint>();

services.AddSingleton<WorkspaceSemanticTokensRefreshPublisher, DefaultWorkspaceSemanticTokensRefreshPublisher>();
services.AddSingleton<IProjectSnapshotChangeTrigger, DefaultWorkspaceSemanticTokensRefreshTrigger>();

// Ensure that we don't add the default service if something else has added one.
services.TryAddSingleton<IRazorSemanticTokensInfoService, RazorSemanticTokensInfoService>();
}

public static void AddCodeActionsServices(this IServiceCollection services)
Expand Down Expand Up @@ -228,7 +231,12 @@ public static void AddDocumentManagementServices(this IServiceCollection service
services.AddSingleton<DocumentProcessedListener, RazorDiagnosticsPublisher>();
}

services.AddSingleton<DocumentProcessedListener, GeneratedDocumentSynchronizer>();
// Don't generate documents in the language server if cohost is enabled, let cohost do it.
if (!featureOptions.UseRazorCohostServer)
{
services.AddSingleton<DocumentProcessedListener, GeneratedDocumentSynchronizer>();
}

services.AddSingleton<DocumentProcessedListener, CodeDocumentReferenceHolder>();

services.AddSingleton<ProjectSnapshotManagerAccessor, DefaultProjectSnapshotManagerAccessor>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ protected override ILspServices ConstructLspServices()
services.AddLifeCycleServices(this, _clientConnection, _lspServerActivationTracker);

services.AddDiagnosticServices();
services.AddSemanticTokensServices();
services.AddSemanticTokensServices(featureOptions);
services.AddDocumentManagementServices(featureOptions);
services.AddCompletionServices(featureOptions);
services.AddFormattingServices();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,30 @@
// 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.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
using Microsoft.AspNetCore.Razor.LanguageServer.EndpointContracts;
using Microsoft.AspNetCore.Razor.Telemetry;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CommonLanguageServerProtocol.Framework;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.LanguageServer.Protocol;

namespace Microsoft.AspNetCore.Razor.LanguageServer.Semantic;

[LanguageServerEndpoint(LspEndpointName)]
internal sealed class SemanticTokensRangeEndpoint(IRazorLoggerFactory loggerFactory, ITelemetryReporter? telemetryReporter)
[LanguageServerEndpoint(Methods.TextDocumentSemanticTokensRangeName)]
internal sealed class SemanticTokensRangeEndpoint(
IRazorSemanticTokensInfoService semanticTokensInfoService,
RazorLSPOptionsMonitor razorLSPOptionsMonitor,
IClientConnection clientConnection)
: IRazorRequestHandler<SemanticTokensRangeParams, SemanticTokens?>, ICapabilitiesProvider
{
private const string LspEndpointName = Methods.TextDocumentSemanticTokensRangeName;

private readonly ILogger _logger = loggerFactory.CreateLogger<SemanticTokensRangeEndpoint>();
private readonly ITelemetryReporter? _telemetryReporter = telemetryReporter;

private RazorSemanticTokensLegend? _razorSemanticTokensLegend;
private readonly IRazorSemanticTokensInfoService _semanticTokensInfoService = semanticTokensInfoService;
private readonly RazorLSPOptionsMonitor _razorLSPOptionsMonitor = razorLSPOptionsMonitor;
private readonly IClientConnection _clientConnection = clientConnection;

public bool MutatesSolutionState { get; } = false;

public void ApplyCapabilities(VSInternalServerCapabilities serverCapabilities, VSInternalClientCapabilities clientCapabilities)
{
_razorSemanticTokensLegend = new RazorSemanticTokensLegend(clientCapabilities);

serverCapabilities.SemanticTokensOptions = new SemanticTokensOptions
{
Full = false,
Legend = _razorSemanticTokensLegend.Legend,
Range = true,
};
_semanticTokensInfoService.ApplyCapabilities(serverCapabilities, clientCapabilities);
}

public TextDocumentIdentifier GetTextDocumentIdentifier(SemanticTokensRangeParams request)
Expand All @@ -47,26 +34,10 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(SemanticTokensRangeParam

public async Task<SemanticTokens?> HandleRequestAsync(SemanticTokensRangeParams request, RazorRequestContext requestContext, CancellationToken cancellationToken)
{
if (request is null)
{
throw new ArgumentNullException(nameof(request));
}

var documentContext = requestContext.GetRequiredDocumentContext();
var semanticTokensInfoService = requestContext.GetRequiredService<IRazorSemanticTokensInfoService>();

var correlationId = Guid.NewGuid();
using var _ = _telemetryReporter?.TrackLspRequest(LspEndpointName, LanguageServerConstants.RazorLanguageServerName, correlationId);
var semanticTokens = await semanticTokensInfoService.GetSemanticTokensAsync(request.TextDocument, request.Range, documentContext, _razorSemanticTokensLegend.AssumeNotNull(), correlationId, cancellationToken).ConfigureAwait(false);
var amount = semanticTokens is null ? "no" : (semanticTokens.Data.Length / 5).ToString(Thread.CurrentThread.CurrentCulture);

_logger.LogInformation("Returned {amount} semantic tokens for range ({startLine},{startChar})-({endLine},{endChar}) in {request.TextDocument.Uri}.", amount, request.Range.Start.Line, request.Range.Start.Character, request.Range.End.Line, request.Range.End.Character, request.TextDocument.Uri);
var colorBackground = _razorLSPOptionsMonitor.CurrentValue.ColorBackground;

if (semanticTokens is not null)
{
Debug.Assert(semanticTokens.Data.Length % 5 == 0, $"Number of semantic token-ints should be divisible by 5. Actual number: {semanticTokens.Data.Length}");
Debug.Assert(semanticTokens.Data.Length == 0 || semanticTokens.Data[0] >= 0, $"Line offset should not be negative.");
}
var semanticTokens = await _semanticTokensInfoService.GetSemanticTokensAsync(_clientConnection, request.TextDocument, request.Range, documentContext, colorBackground, cancellationToken).ConfigureAwait(false);

return semanticTokens;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.LanguageServer.Protocol;

namespace Microsoft.AspNetCore.Razor.LanguageServer.Semantic;

internal interface IRazorSemanticTokensInfoService
internal interface IRazorSemanticTokensInfoService : ICapabilitiesProvider
{
Task<SemanticTokens?> GetSemanticTokensAsync(TextDocumentIdentifier textDocumentIdentifier, Range range, VersionedDocumentContext documentContext, RazorSemanticTokensLegend razorSemanticTokensLegend, Guid correlationId, CancellationToken cancellationToken);
Task<SemanticTokens?> GetSemanticTokensAsync(IClientConnection clientConnection, TextDocumentIdentifier textDocumentIdentifier, Range range, VersionedDocumentContext documentContext, bool colorBackground, CancellationToken cancellationToken);
}
Loading

0 comments on commit 55551d6

Please sign in to comment.