| File: Language\CodeGeneration\CodeRenderingContext.cs | Web Access |
| Project: src\roslyn\src\Razor\src\Compiler\Microsoft.CodeAnalysis.Razor.Compiler\src\Microsoft.CodeAnalysis.Razor.Compiler.csproj (Microsoft.CodeAnalysis.Razor.Compiler) |
// 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.Collections.Generic; using System.Collections.Immutable; using Microsoft.AspNetCore.Razor.Language.Intermediate; using Microsoft.AspNetCore.Razor.PooledObjects; namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration; public sealed class CodeRenderingContext : IDisposable { private readonly record struct ScopeInternal(IntermediateNodeWriter Writer); public RazorSourceDocument SourceDocument { get; } public RazorCodeGenerationOptions Options { get; } public CodeWriter CodeWriter { get; } private readonly DocumentIntermediateNode _documentNode; private readonly Stack<IntermediateNode> _ancestorStack; private readonly Stack<ScopeInternal> _scopeStack; private readonly ImmutableArray<RazorDiagnostic>.Builder _diagnostics; private readonly ImmutableArray<SourceMapping>.Builder _sourceMappings; private readonly ImmutableArray<LinePragma>.Builder _linePragmas; private IntermediateNodeVisitor? _visitor; public IntermediateNodeVisitor Visitor => _visitor.AssumeNotNull(); public string DocumentKind => _documentNode.DocumentKind; public CodeRenderingContext( IntermediateNodeWriter nodeWriter, RazorSourceDocument sourceDocument, DocumentIntermediateNode documentNode, RazorCodeGenerationOptions options) { ArgHelper.ThrowIfNull(nodeWriter); ArgHelper.ThrowIfNull(sourceDocument); ArgHelper.ThrowIfNull(documentNode); ArgHelper.ThrowIfNull(options); SourceDocument = sourceDocument; _documentNode = documentNode; Options = options; _ancestorStack = StackPool<IntermediateNode>.Default.Get(); _scopeStack = StackPool<ScopeInternal>.Default.Get(); _scopeStack.Push(new(nodeWriter)); _diagnostics = ArrayBuilderPool<RazorDiagnostic>.Default.Get(); foreach (var diagnostic in _documentNode.GetAllDiagnostics()) { _diagnostics.Add(diagnostic); } _linePragmas = ArrayBuilderPool<LinePragma>.Default.Get(); _sourceMappings = ArrayBuilderPool<SourceMapping>.Default.Get(); CodeWriter = new CodeWriter(options); } public void Dispose() { StackPool<IntermediateNode>.Default.Return(_ancestorStack); StackPool<ScopeInternal>.Default.Return(_scopeStack); ArrayBuilderPool<RazorDiagnostic>.Default.Return(_diagnostics); ArrayBuilderPool<LinePragma>.Default.Return(_linePragmas); ArrayBuilderPool<SourceMapping>.Default.Return(_sourceMappings); CodeWriter.Dispose(); } // This will be called by the document writer when the context is 'live'. public void SetVisitor(IntermediateNodeVisitor visitor) { _visitor = visitor; } public IntermediateNodeWriter NodeWriter => _scopeStack.Peek().Writer; public IntermediateNode? Parent => _ancestorStack.Count == 0 ? null : _ancestorStack.Peek(); public void AddDiagnostic(RazorDiagnostic diagnostic) { _diagnostics.Add(diagnostic); } public ImmutableArray<RazorDiagnostic> GetDiagnostics() { var warningLevel = Options.RazorWarningLevel; // Filter out diagnostics whose warning level exceeds the configured level. // Diagnostics with level 0 are always reported regardless of the configured level. using var filtered = new PooledArrayBuilder<RazorDiagnostic>(capacity: _diagnostics.Count); foreach (var diagnostic in _diagnostics) { if (diagnostic.WarningLevel <= warningLevel) { filtered.Add(diagnostic); } } return filtered.ToImmutableOrderedBy(static d => d.Span.AbsoluteIndex); } public void AddSourceMappingFor(IntermediateNode node) { ArgHelper.ThrowIfNull(node); if (node.Source is not SourceSpan nodeSource) { return; } AddSourceMappingFor(nodeSource); } public void AddSourceMappingFor(SourceSpan source, int offset = 0) { if (SourceDocument.FilePath != null && !string.Equals(SourceDocument.FilePath, source.FilePath, StringComparison.OrdinalIgnoreCase)) { // We don't want to generate line mappings for imports. return; } var currentLocation = CodeWriter.Location with { AbsoluteIndex = CodeWriter.Location.AbsoluteIndex + offset, CharacterIndex = CodeWriter.Location.CharacterIndex + offset }; var endCharacterIndex = (source.LineCount == 0) ? currentLocation.CharacterIndex + source.Length : source.EndCharacterIndex; var generatedLocation = new SourceSpan( currentLocation.FilePath, currentLocation.AbsoluteIndex, currentLocation.LineIndex, currentLocation.CharacterIndex, source.Length, lineCount: source.LineCount, endCharacterIndex: endCharacterIndex); var sourceMapping = new SourceMapping(source, generatedLocation); _sourceMappings.Add(sourceMapping); } public ImmutableArray<SourceMapping> GetSourceMappings() => _sourceMappings.ToImmutableAndClear(); public void RenderChildren(IntermediateNode node) { ArgHelper.ThrowIfNull(node); _ancestorStack.Push(node); for (var i = 0; i < node.Children.Count; i++) { Visitor.Visit(node.Children[i]); } _ancestorStack.Pop(); } public void RenderChildren(IntermediateNode node, IntermediateNodeWriter writer) { ArgHelper.ThrowIfNull(node); ArgHelper.ThrowIfNull(writer); _scopeStack.Push(new ScopeInternal(writer)); _ancestorStack.Push(node); for (var i = 0; i < node.Children.Count; i++) { Visitor.Visit(node.Children[i]); } _ancestorStack.Pop(); _scopeStack.Pop(); } public void RenderNode(IntermediateNode node) { ArgHelper.ThrowIfNull(node); Visitor.Visit(node); } public void RenderNode(IntermediateNode node, IntermediateNodeWriter writer) { ArgHelper.ThrowIfNull(node); ArgHelper.ThrowIfNull(writer); _scopeStack.Push(new ScopeInternal(writer)); Visitor.Visit(node); _scopeStack.Pop(); } public void AddLinePragma(LinePragma linePragma) { _linePragmas.Add(linePragma); } public ImmutableArray<LinePragma> GetLinePragmas() => _linePragmas.ToImmutableAndClear(); public void PushAncestor(IntermediateNode node) { _ancestorStack.Push(node); } }