Move GetSyntaxTree to document snapshot

This commit is contained in:
David Wengier 2024-08-21 10:17:06 +10:00
Родитель 53fec86c28
Коммит a3edec0c8a
9 изменённых файлов: 35 добавлений и 51 удалений

Просмотреть файл

@ -1,19 +1,9 @@
// 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;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.GoToDefinition;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Workspaces;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Definition;
@ -24,10 +14,4 @@ internal sealed class RazorComponentDefinitionService(
ILoggerFactory loggerFactory)
: AbstractRazorComponentDefinitionService(componentSearchEngine, documentMappingService, loggerFactory.GetOrCreateLogger<RazorComponentDefinitionService>())
{
protected override async ValueTask<SyntaxTree> GetCSharpSyntaxTreeAsync(IDocumentSnapshot documentSnapshot, CancellationToken cancellationToken)
{
var codeDocument = await documentSnapshot.GetGeneratedOutputAsync().ConfigureAwait(false);
var csharpText = codeDocument.GetCSharpSourceText();
return CSharpSyntaxTree.ParseText(csharpText, cancellationToken: cancellationToken);
}
}

Просмотреть файл

@ -69,11 +69,8 @@ internal abstract class AbstractRazorComponentDefinitionService(
{
_logger.LogInformation($"Attempting to get definition from an attribute directly.");
var originCodeDocument = await documentSnapshot.GetGeneratedOutputAsync().ConfigureAwait(false);
var syntaxTree = await GetCSharpSyntaxTreeAsync(documentSnapshot, cancellationToken).ConfigureAwait(false);
var range = await RazorComponentDefinitionHelpers
.TryGetPropertyRangeAsync(originCodeDocument, syntaxTree, attributeDescriptor.GetPropertyName(), _documentMappingService, _logger, cancellationToken)
.TryGetPropertyRangeAsync(documentSnapshot, attributeDescriptor.GetPropertyName(), _documentMappingService, _logger, cancellationToken)
.ConfigureAwait(false);
if (range is not null)
@ -88,6 +85,4 @@ internal abstract class AbstractRazorComponentDefinitionService(
// at least then press F7 to go there.
return VsLspFactory.DefaultRange;
}
protected abstract ValueTask<SyntaxTree> GetCSharpSyntaxTreeAsync(IDocumentSnapshot documentSnapshot, CancellationToken cancellationToken);
}

Просмотреть файл

@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using LspRange = Microsoft.VisualStudio.LanguageServer.Protocol.Range;
@ -129,8 +130,7 @@ internal static class RazorComponentDefinitionHelpers
}
public static async Task<LspRange?> TryGetPropertyRangeAsync(
RazorCodeDocument codeDocument,
SyntaxTree csharpSyntaxTree,
IDocumentSnapshot documentSnapshot,
string propertyName,
IDocumentMappingService documentMappingService,
ILogger logger,
@ -148,7 +148,9 @@ internal static class RazorComponentDefinitionHelpers
// will error, but allowing them to Go To Def on that property regardless, actually helps
// them fix the error.
var csharpSyntaxTree = await documentSnapshot.GetCSharpSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
var root = await csharpSyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false);
var codeDocument = await documentSnapshot.GetGeneratedOutputAsync().ConfigureAwait(false);
// Since we know how the compiler generates the C# source we can be a little specific here, and avoid
// long tree walks. If the compiler ever changes how they generate their code, the tests for this will break

Просмотреть файл

@ -3,8 +3,10 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem;
@ -62,4 +64,11 @@ internal class DocumentSnapshot : IDocumentSnapshot
{
return new DocumentSnapshot(ProjectInternal, State.WithText(text, VersionStamp.Create()));
}
public async Task<SyntaxTree> GetCSharpSyntaxTreeAsync(CancellationToken cancellationToken)
{
var codeDocument = await GetGeneratedOutputAsync().ConfigureAwait(false);
var csharpText = codeDocument.GetCSharpSourceText();
return CSharpSyntaxTree.ParseText(csharpText, cancellationToken: cancellationToken);
}
}

Просмотреть файл

@ -2,6 +2,7 @@
// Licensed under the MIT license. See License.txt in the project root for license information.
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Text;
@ -20,6 +21,12 @@ internal interface IDocumentSnapshot
Task<VersionStamp> GetTextVersionAsync();
Task<RazorCodeDocument> GetGeneratedOutputAsync();
/// <summary>
/// Gets the Roslyn syntax tree for the generated C# for this Razor document
/// </summary>
/// <remarks>Using this from the LSP server side of things is not ideal. Use sparingly :)</remarks>
Task<SyntaxTree> GetCSharpSyntaxTreeAsync(CancellationToken cancellationToken);
bool TryGetText([NotNullWhen(true)] out SourceText? result);
bool TryGetTextVersion(out VersionStamp result);
bool TryGetGeneratedOutput([NotNullWhen(true)] out RazorCodeDocument? result);

Просмотреть файл

@ -4,6 +4,7 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Text;
@ -75,4 +76,7 @@ internal class ImportDocumentSnapshot : IDocumentSnapshot
public IDocumentSnapshot WithText(SourceText text)
=> throw new NotSupportedException();
public Task<SyntaxTree> GetCSharpSyntaxTreeAsync(CancellationToken cancellationToken)
=> throw new NotSupportedException();
}

Просмотреть файл

@ -2,16 +2,10 @@
// Licensed under the MIT license. See License.txt in the project root for license information.
using System.Composition;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.GoToDefinition;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.CodeAnalysis.Remote.Razor.ProjectSystem;
namespace Microsoft.CodeAnalysis.Remote.Razor.GoToDefinition;
@ -23,19 +17,4 @@ internal sealed class RazorComponentDefinitionService(
ILoggerFactory loggerFactory)
: AbstractRazorComponentDefinitionService(componentSearchEngine, documentMappingService, loggerFactory.GetOrCreateLogger<RazorComponentDefinitionService>())
{
protected override async ValueTask<SyntaxTree> GetCSharpSyntaxTreeAsync(IDocumentSnapshot documentSnapshot, CancellationToken cancellationToken)
{
Debug.Assert(documentSnapshot is RemoteDocumentSnapshot, "This method only works on document snapshots created in the OOP process");
var remoteSnapshot = (RemoteDocumentSnapshot)documentSnapshot;
var document = await remoteSnapshot.GetGeneratedDocumentAsync().ConfigureAwait(false);
if (document.TryGetSyntaxTree(out var syntaxTree))
{
return syntaxTree;
}
var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
return tree.AssumeNotNull();
}
}

Просмотреть файл

@ -117,4 +117,11 @@ internal class RemoteDocumentSnapshot(TextDocument textDocument, RemoteProjectSn
// HACK: We're not in the same solution fork as the LSP server that provides content for this document
return generatedDocument.WithText(csharpSourceText);
}
public async Task<SyntaxTree> GetCSharpSyntaxTreeAsync(CancellationToken cancellationToken)
{
var document = await GetGeneratedDocumentAsync().ConfigureAwait(false);
var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
return tree.AssumeNotNull();
}
}

Просмотреть файл

@ -1,13 +1,12 @@
// 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.AspNetCore.Razor.LanguageServer.Completion;
using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.AspNetCore.Razor.Test.Common.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.GoToDefinition;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.VisualStudio.LanguageServer.Protocol;
@ -391,13 +390,11 @@ public class RazorComponentDefinitionHelpersTest(ITestOutputHelper testOutput) :
var codeDocument = CreateCodeDocument(content);
var expectedRange = codeDocument.Source.Text.GetRange(selection);
var snapshot = TestDocumentSnapshot.Create("test.razor", content).With(codeDocument);
var documentMappingService = new LspDocumentMappingService(FilePathService, new TestDocumentContextFactory(), LoggerFactory);
var csharpText = codeDocument.GetCSharpSourceText();
var syntaxTree = CSharpSyntaxTree.ParseText(csharpText);
var range = await RazorComponentDefinitionHelpers.TryGetPropertyRangeAsync(codeDocument, syntaxTree, propertyName, documentMappingService, Logger, DisposalToken);
var range = await RazorComponentDefinitionHelpers.TryGetPropertyRangeAsync(snapshot, propertyName, documentMappingService, Logger, DisposalToken);
Assert.NotNull(range);
Assert.Equal(expectedRange, range);
}