Skip to content

[automated] Merge branch 'main-vs-deps' => 'release/dev18.0' #77692

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

Merged
Merged
Show file tree
Hide file tree
Changes from 9 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
2 changes: 1 addition & 1 deletion azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ stages:
- powershell: eng/build.ps1 -configuration Release -prepareMachine -ci -restore -binaryLogName Restore.binlog
displayName: Restore

- powershell: eng/build.ps1 -configuration Release -prepareMachine -ci -build -pack -publish -sign -binaryLogName Build.binlog /p:DotnetPublishUsingPipelines=true
- powershell: eng/build.ps1 -configuration Release -prepareMachine -ci -build -pack -publish -sign -binaryLogName Build.binlog /p:DotnetPublishUsingPipelines=true /p:ContinuousIntegrationBuildCorrectness=true
displayName: Build

# While this task is not executed in the official build, this serves as a PR check for whether symbol exclusions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ private static async Task TestSymbolFoundAsync(string inputLine, string code)
var reparsedResult = await StackTraceAnalyzer.AnalyzeAsync(stackFrame.ToString(), CancellationToken.None);
Assert.Single(reparsedResult.ParsedFrames);

var reparsedFrame = reparsedResult.ParsedFrames[0] as ParsedStackFrame;
AssertEx.NotNull(reparsedFrame);
var reparsedFrame = Assert.IsType<ParsedStackFrame>(reparsedResult.ParsedFrames[0]);
StackFrameUtils.AssertEqual(stackFrame.Root, reparsedFrame.Root);

// Get the definition for the parsed frame
Expand Down Expand Up @@ -820,9 +819,9 @@ class C
var result = await StackTraceAnalyzer.AnalyzeAsync(line, CancellationToken.None);
Assert.Equal(1, result.ParsedFrames.Length);

var parsedFame = result.ParsedFrames.OfType<ParsedStackFrame>().Single();
var parsedFrame = Assert.IsType<ParsedStackFrame>(result.ParsedFrames[0]);
var service = workspace.Services.GetRequiredService<IStackTraceExplorerService>();
var definition = await service.TryFindDefinitionAsync(workspace.CurrentSolution, parsedFame, StackFrameSymbolPart.Method, CancellationToken.None);
var definition = await service.TryFindDefinitionAsync(workspace.CurrentSolution, parsedFrame, StackFrameSymbolPart.Method, CancellationToken.None);
Assert.Null(definition);
}

Expand Down Expand Up @@ -850,13 +849,89 @@ public async Task TestMetadataSymbol()
var result = await StackTraceAnalyzer.AnalyzeAsync("at System.String.ToLower()", CancellationToken.None);
Assert.Single(result.ParsedFrames);

var frame = result.ParsedFrames[0] as ParsedStackFrame;
AssertEx.NotNull(frame);

var frame = Assert.IsType<ParsedStackFrame>(result.ParsedFrames[0]);
var service = workspace.Services.GetRequiredService<IStackTraceExplorerService>();
var definition = await service.TryFindDefinitionAsync(workspace.CurrentSolution, frame, StackFrameSymbolPart.Method, CancellationToken.None);

AssertEx.NotNull(definition);
Assert.Equal("String.ToLower", definition.NameDisplayParts.ToVisibleDisplayString(includeLeftToRightMarker: false));
}

[Fact]
public async Task TestAdditionalFileExactMatchAsync()
{
using var workspace = TestWorkspace.Create(
"""
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
class C
{
void M() {}
}
</Document>
<AdditionalDocument FilePath="C:/path/to/Component.razor">
@page "/"

@code
{
void M()
{
}
}
</AdditionalDocument>
</Project>
</Workspace>
""");

var result = await StackTraceAnalyzer.AnalyzeAsync("at Path.To.Component.M() in C:/path/to/Component.razor:line 5", CancellationToken.None);
Assert.Single(result.ParsedFrames);

var frame = Assert.IsType<ParsedStackFrame>(result.ParsedFrames[0]);
var service = workspace.Services.GetRequiredService<IStackTraceExplorerService>();
var (document, line) = service.GetDocumentAndLine(workspace.CurrentSolution, frame);
Assert.Equal(5, line);

AssertEx.NotNull(document);
Assert.Equal(@"C:/path/to/Component.razor", document.FilePath);
}

[Fact]
public async Task TestAdditionalFileNameMatchAsync()
{
using var workspace = TestWorkspace.Create(
"""
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
class C
{
void M() {}
}
</Document>
<AdditionalDocument FilePath="C:/path/to/Component.razor" Name="Component.razor">
@page "/"

@code
{
void M()
{
}
}
</AdditionalDocument>
</Project>
</Workspace>
""");

var result = await StackTraceAnalyzer.AnalyzeAsync("at Path.To.Component.M() in Component.razor:line 5", CancellationToken.None);
Assert.Single(result.ParsedFrames);

var frame = Assert.IsType<ParsedStackFrame>(result.ParsedFrames[0]);
var service = workspace.Services.GetRequiredService<IStackTraceExplorerService>();
var (document, line) = service.GetDocumentAndLine(workspace.CurrentSolution, frame);
Assert.Equal(5, line);

AssertEx.NotNull(document);
Assert.Equal(@"C:/path/to/Component.razor", document.FilePath);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ private static ImmutableArray<TextDocument> GetFileMatches(Solution solution, St

if (documentId is not null)
{
var document = solution.GetRequiredDocument(documentId);
var document = solution.GetRequiredTextDocument(documentId);
return [document];
}

Expand All @@ -105,7 +105,8 @@ private static ImmutableArray<TextDocument> GetFileMatches(Solution solution, St

foreach (var document in allDocuments)
{
if (string.Equals(document.Name, documentName, StringComparison.OrdinalIgnoreCase))
var name = Path.GetFileName(document.Name);
if (name.Equals(documentName, StringComparison.OrdinalIgnoreCase))
{
potentialMatches.Add(document);
}
Expand Down
7 changes: 5 additions & 2 deletions src/Features/Lsif/Generator/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -466,19 +466,22 @@ private static async Task GenerateSemanticTokensAsync(
IdFactory idFactory,
LsifDocument documentVertex)
{
var cancellationToken = CancellationToken.None;

// Compute colorization data.
//
// Unlike the mainline LSP scenario, where we control both the syntactic colorizer (in-proc syntax tagger)
// and the semantic colorizer (LSP semantic tokens) LSIF is more likely to be consumed by clients which may
// have different syntactic classification behavior than us, resulting in missing colors. To avoid this, we
// include syntax tokens in the generated data.
var text = await document.GetTextAsync(cancellationToken);
var data = await SemanticTokensHelpers.ComputeSemanticTokensDataAsync(
// Just get the pure-lsp semantic tokens here.
document,
spans: [],
spans: [text.Lines.GetLinePositionSpan(new TextSpan(0, text.Length))],
supportsVisualStudioExtensions: true,
options: Classification.ClassificationOptions.Default,
cancellationToken: CancellationToken.None);
cancellationToken);

var semanticTokensResult = new SemanticTokensResult(new SemanticTokens { Data = data }, idFactory);
var semanticTokensEdge = Edge.Create(Methods.TextDocumentSemanticTokensFullName, documentVertex.GetId(), semanticTokensResult.GetId(), idFactory);
Expand Down
7 changes: 5 additions & 2 deletions src/LanguageServer/Protocol/DefaultCapabilitiesProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,13 @@ public ServerCapabilities GetCapabilities(ClientCapabilities clientCapabilities)
// Using only range handling has shown to be more performant than using a combination of full/edits/range
// handling, especially for larger files. With range handling, we only need to compute tokens for whatever
// is in view, while with full/edits handling we need to compute tokens for the entire file and then
// potentially run a diff between the old and new tokens.
// potentially run a diff between the old and new tokens. Therefore, we only enable full handling if
// the client does not support ranges.
var rangeCapabilities = clientCapabilities.TextDocument?.SemanticTokens?.Requests?.Range;
var supportsSemanticTokensRange = rangeCapabilities?.Value is not (false or null);
capabilities.SemanticTokensOptions = new SemanticTokensOptions
{
Full = false,
Full = !supportsSemanticTokensRange,
Range = true,
Legend = new SemanticTokensLegend
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,37 +12,35 @@
namespace Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Razor;

[Method(SemanticRangesMethodName)]
internal class SemanticTokensRangesHandler : ILspServiceDocumentRequestHandler<SemanticTokensRangesParams, SemanticTokens>
internal sealed class SemanticTokensRangesHandler(
IGlobalOptionService globalOptions,
SemanticTokensRefreshQueue semanticTokensRefreshQueue)
: ILspServiceDocumentRequestHandler<SemanticTokensRangesParams, SemanticTokens>
{
public const string SemanticRangesMethodName = "roslyn/semanticTokenRanges";
private readonly IGlobalOptionService _globalOptions;
private readonly SemanticTokensRefreshQueue _semanticTokenRefreshQueue;

private readonly IGlobalOptionService _globalOptions = globalOptions;
private readonly SemanticTokensRefreshQueue _semanticTokenRefreshQueue = semanticTokensRefreshQueue;

public bool MutatesSolutionState => false;

public bool RequiresLSPSolution => true;

public SemanticTokensRangesHandler(
IGlobalOptionService globalOptions,
SemanticTokensRefreshQueue semanticTokensRefreshQueue)
{
_globalOptions = globalOptions;
_semanticTokenRefreshQueue = semanticTokensRefreshQueue;
}

public TextDocumentIdentifier GetTextDocumentIdentifier(SemanticTokensRangesParams request)
{
Contract.ThrowIfNull(request.TextDocument);
return request.TextDocument;
}

public async Task<SemanticTokens> HandleRequestAsync(
SemanticTokensRangesParams request,
RequestContext context,
CancellationToken cancellationToken)
SemanticTokensRangesParams request,
RequestContext context,
CancellationToken cancellationToken)
{
Contract.ThrowIfNull(request.TextDocument, "TextDocument is null.");
var tokensData = await SemanticTokensHelpers.HandleRequestHelperAsync(_globalOptions, _semanticTokenRefreshQueue, request.Ranges, context, cancellationToken).ConfigureAwait(false);

var tokensData = await SemanticTokensHelpers.HandleRequestHelperAsync(
_globalOptions, _semanticTokenRefreshQueue, request.Ranges, context, cancellationToken).ConfigureAwait(false);
return new SemanticTokens { Data = tokensData };
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Options;
using Roslyn.LanguageServer.Protocol;
using LSP = Roslyn.LanguageServer.Protocol;

namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SemanticTokens;

[Method(Methods.TextDocumentSemanticTokensFullName)]
internal sealed class SemanticTokensFullHandler(
IGlobalOptionService globalOptions,
SemanticTokensRefreshQueue semanticTokensRefreshQueue)
: ILspServiceDocumentRequestHandler<SemanticTokensFullParams, LSP.SemanticTokens>
{
private readonly IGlobalOptionService _globalOptions = globalOptions;
private readonly SemanticTokensRefreshQueue _semanticTokenRefreshQueue = semanticTokensRefreshQueue;

public bool MutatesSolutionState => false;
public bool RequiresLSPSolution => true;

public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.SemanticTokensFullParams request)
{
Contract.ThrowIfNull(request.TextDocument);
return request.TextDocument;
}

public async Task<LSP.SemanticTokens> HandleRequestAsync(
SemanticTokensFullParams request,
RequestContext context,
CancellationToken cancellationToken)
{
Contract.ThrowIfNull(request.TextDocument);

// Passing an null array of ranges will cause the helper to return tokens for the entire document.
var tokensData = await SemanticTokensHelpers.HandleRequestHelperAsync(
_globalOptions, _semanticTokenRefreshQueue, ranges: null, context, cancellationToken).ConfigureAwait(false);
return new LSP.SemanticTokens { Data = tokensData };
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Composition;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Options;

namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SemanticTokens;

[ExportCSharpVisualBasicLspServiceFactory(typeof(SemanticTokensFullHandler)), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class SemanticTokensFullHandlerFactory(IGlobalOptionService globalOptions) : ILspServiceFactory
{
private readonly IGlobalOptionService _globalOptions = globalOptions;

public ILspService CreateILspService(LspServices lspServices, WellKnownLspServerKinds serverKind)
{
var semanticTokensRefreshQueue = lspServices.GetRequiredService<SemanticTokensRefreshQueue>();
return new SemanticTokensFullHandler(_globalOptions, semanticTokensRefreshQueue);
}
}
Loading
Loading