| File: ProjectBackedFormattingSnapshot.cs | Web Access |
| Project: Microsoft.CodeAnalysis.Razor.DotNetFormat.csproj (Microsoft.CodeAnalysis.Razor.DotNetFormat) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Razor.Language; using Microsoft.CodeAnalysis.Remote.Razor.Formatting; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Razor.Formatting; // Adapts a Razor AdditionalDocument to the shared formatting snapshot contract. Generation stays on Roslyn's normal // Project pipeline so dotnet format shares the project's incremental generator state instead of creating a GeneratorDriver. internal sealed class ProjectBackedFormattingSnapshot : IDocumentSnapshot { private RazorCodeDocument? _codeDocument; private RazorProjectGeneratorRunResult? _generatorRunResult; private readonly TextDocument _document; private ProjectBackedFormattingSnapshot( TextDocument document, RazorProjectGeneratorRunResult? generatorRunResult, RazorCodeDocument? codeDocument) { _document = document; _generatorRunResult = generatorRunResult; _codeDocument = codeDocument; } public RazorCodeDocument CodeDocument => _codeDocument ?? throw new InvalidOperationException("The Razor code document has not been generated."); public string FilePath => _document.FilePath ?? throw new InvalidOperationException("The Razor document does not have a file path."); public RazorFileKind FileKind => FileKinds.GetFileKindFromPath(FilePath); public async ValueTask<RazorCodeDocument> GetGeneratedOutputAsync(CancellationToken cancellationToken) { if (_codeDocument is null) { _generatorRunResult = await RazorProjectGeneratorRunResult.TryCreateAsync(_document.Project, cancellationToken).ConfigureAwait(false) ?? throw new InvalidOperationException($"The Razor source generator was no longer available for project '{_document.Project.Name}'."); _codeDocument = _generatorRunResult.GetRequiredCodeDocument(FilePath); } return _codeDocument; } public async ValueTask<SyntaxTree> GetCSharpSyntaxTreeAsync( bool declarationDocument, CancellationToken cancellationToken) { _generatorRunResult ??= await RazorProjectGeneratorRunResult.TryCreateAsync(_document.Project, cancellationToken).ConfigureAwait(false) ?? throw new InvalidOperationException($"The Razor source generator was no longer available for project '{_document.Project.Name}'."); var generatedDocument = await _generatorRunResult.GetRequiredSourceGeneratedDocumentAsync( FilePath, declarationDocument, cancellationToken).ConfigureAwait(false); return await generatedDocument.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false) ?? throw new InvalidOperationException($"Generated document '{generatedDocument.Name}' did not have a syntax tree."); } public IDocumentSnapshot WithText(SourceText text) { // Replacing the AdditionalDocument in a new Solution snapshot invalidates the relevant incremental generator // inputs. Clear the cached run result so the next request observes outputs for this updated project snapshot. var updatedDocument = _document.Project.Solution .WithAdditionalDocumentText(_document.Id, text) .GetAdditionalDocument(_document.Id) ?? throw new InvalidOperationException($"Could not update Razor document '{FilePath}'."); return new ProjectBackedFormattingSnapshot( updatedDocument, generatorRunResult: null, codeDocument: null); } public static async Task<ProjectBackedFormattingSnapshot?> TryCreateAsync( TextDocument document, CancellationToken cancellationToken) { var generatorResult = await RazorProjectGeneratorRunResult.TryCreateAsync(document.Project, cancellationToken).ConfigureAwait(false); if (generatorResult is null) { return null; } var filePath = document.FilePath ?? throw new InvalidOperationException("The Razor document does not have a file path."); var codeDocument = generatorResult.GetRequiredCodeDocument(filePath); return new(document, generatorResult, codeDocument); } }