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

Self-versioned documents #10747

Merged
merged 18 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -11,15 +11,15 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces.Protocol.SemanticTokens;
internal class ProvideSemanticTokensRangesParams : SemanticTokensParams
{
[JsonPropertyName("requiredHostDocumentVersion")]
public long RequiredHostDocumentVersion { get; }
public int RequiredHostDocumentVersion { get; }

[JsonPropertyName("ranges")]
public Range[] Ranges { get; }

[JsonPropertyName("correlationId")]
public Guid CorrelationId { get; }

public ProvideSemanticTokensRangesParams(TextDocumentIdentifier textDocument, long requiredHostDocumentVersion, Range[] ranges, Guid correlationId)
public ProvideSemanticTokensRangesParams(TextDocumentIdentifier textDocument, int requiredHostDocumentVersion, Range[] ranges, Guid correlationId)
{
TextDocument = textDocument;
RequiredHostDocumentVersion = requiredHostDocumentVersion;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ private static ImmutableArray<SemanticRange> CombineSemanticRanges(ImmutableArra
csharpRanges = [csharpRange];
}

_logger.LogDebug($"Requesting C# semantic tokens for host version {documentContext.Version}, correlation ID {correlationId}, and the server thinks there are {codeDocument.GetCSharpSourceText().Lines.Count} lines of C#");

var csharpResponse = await _csharpSemanticTokensProvider.GetCSharpSemanticTokensResponseAsync(documentContext, csharpRanges, correlationId, cancellationToken).ConfigureAwait(false);

// Indicates an issue with retrieving the C# response (e.g. no response or C# is out of sync with us).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ private async Task<SynchronizedResult<TVirtualDocumentSnapshot>> TrySynchronizeV
int requiredHostDocumentVersion,
TextDocumentIdentifier hostDocument,
CancellationToken cancellationToken,
Range? _ = null,
LanguageServer.Protocol.Range? _ = null,
bool rejectOnNewerParallelRequest = true,
[CallerMemberName] string? caller = null)
where TVirtualDocumentSnapshot : VirtualDocumentSnapshot
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.CodeAnalysis.Razor.Workspaces.Protocol.SemanticTokens;
Expand Down Expand Up @@ -58,18 +57,10 @@ internal partial class RazorCustomMessageTarget
SemanticTokensParams requestParams,
CancellationToken cancellationToken)
{
if (semanticTokensParams is null)
{
throw new ArgumentNullException(nameof(semanticTokensParams));
}

if (semanticTokensParams.Ranges is null)
{
throw new ArgumentNullException(nameof(semanticTokensParams.Ranges));
}
_logger.LogDebug($"Semantic tokens request for {semanticTokensParams.Ranges.Max(r => r.End.Line)} max line number, host version {semanticTokensParams.RequiredHostDocumentVersion}, correlation ID {semanticTokensParams.CorrelationId}");
davidwengier marked this conversation as resolved.
Show resolved Hide resolved

var (synchronized, csharpDoc) = await TrySynchronizeVirtualDocumentAsync<CSharpVirtualDocumentSnapshot>(
(int)semanticTokensParams.RequiredHostDocumentVersion,
semanticTokensParams.RequiredHostDocumentVersion,
semanticTokensParams.TextDocument,
cancellationToken,
semanticTokensParams.Ranges.FirstOrDefault());
Expand All @@ -79,54 +70,40 @@ internal partial class RazorCustomMessageTarget
return null;
}

if (synchronized && csharpDoc.HostDocumentSyncVersion == 1)
{
// HACK: Workaround for https://github.com/dotnet/razor/issues/9197 to stop Roslyn NFWs
// Sometimes we get asked for semantic tokens on v1, and we have sent a v1 to Roslyn, but its the wrong v1.
// To prevent Roslyn throwing, let's validate the range we're asking about with the generated document they
// would have seen.
var lastGeneratedDocumentLine = requestParams switch
{
SemanticTokensRangeParams range => range.Range.End.Line,
SemanticTokensRangesParams ranges => ranges.Ranges[^1].End.Line,
_ => Assumed.Unreachable<int>()
};

if (csharpDoc.Snapshot.LineCount < lastGeneratedDocumentLine)
{
_logger.LogWarning($"Pretending we're not synced because we're asking for line {lastGeneratedDocumentLine} but there are only {csharpDoc.Snapshot.LineCount} lines in the buffer.");
Debug.Fail("Rejecting!");
// We report this as a fail to synchronize, as that's essentially what it is: We were asked for v1, with X lines
// and whilst we have v1, we don't have X lines, so we need to wait for a future update to arrive and give us
// more content.
return new ProvideSemanticTokensResponse(tokens: null, -1);
}
}

if (!synchronized)
{
// If we're unable to synchronize we won't produce useful results, but we have to indicate
// it's due to out of sync by providing the old version
return new ProvideSemanticTokensResponse(tokens: null, hostDocumentSyncVersion: csharpDoc.HostDocumentSyncVersion ?? -1);
}

semanticTokensParams.TextDocument.Uri = csharpDoc.Uri;
requestParams.TextDocument.Uri = csharpDoc.Uri;
var textBuffer = csharpDoc.Snapshot.TextBuffer;

_logger.LogDebug($"Requesting semantic tokens for {csharpDoc.Uri}, for buffer version {textBuffer.CurrentSnapshot.Version.VersionNumber} and snapshot version {csharpDoc.Snapshot.Version.VersionNumber}, host version {semanticTokensParams.RequiredHostDocumentVersion}, correlation ID {semanticTokensParams.CorrelationId}");

cancellationToken.ThrowIfCancellationRequested();
var languageServerName = RazorLSPConstants.RazorCSharpLanguageServerName;

SemanticTokens? response;
using (var disposable = _telemetryReporter.TrackLspRequest(lspMethodName, languageServerName, semanticTokensParams.CorrelationId))
{
try
{
var result = await _requestInvoker.ReinvokeRequestOnServerAsync<SemanticTokensParams, SemanticTokens?>(
textBuffer,
lspMethodName,
languageServerName,
requestParams,
cancellationToken).ConfigureAwait(false);

response = result?.Response;
response = result?.Response;
}
catch
{
_logger.LogWarning($"Error getting semantic tokens from Roslyn for host version {semanticTokensParams.RequiredHostDocumentVersion}, correlation ID {semanticTokensParams.CorrelationId}");
throw;
}
}

if (response?.Data is null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,10 @@
namespace Microsoft.VisualStudio.Razor.LanguageClient;

[Export(typeof(IRazorCSharpInterceptionMiddleLayer))]
internal class RazorCSharpInterceptionMiddleLayer : IRazorCSharpInterceptionMiddleLayer
[method: ImportingConstructor]
internal class RazorCSharpInterceptionMiddleLayer(LSPRequestInvoker requestInvoker) : IRazorCSharpInterceptionMiddleLayer
{
private readonly LSPRequestInvoker _requestInvoker;

[ImportingConstructor]
public RazorCSharpInterceptionMiddleLayer(LSPRequestInvoker requestInvoker)
{
_requestInvoker = requestInvoker;
}
private readonly LSPRequestInvoker _requestInvoker = requestInvoker;

public bool CanHandle(string methodName)
=> methodName.Equals(Methods.WorkspaceSemanticTokensRefreshName);
Expand Down