// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using Microsoft.AspNetCore.Razor.Language.Intermediate; namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration { internal class DefaultCodeRenderingContext : CodeRenderingContext { private readonly Stack _ancestors; private readonly RazorCodeDocument _codeDocument; private readonly DocumentIntermediateNode _documentNode; private readonly List _scopes; public DefaultCodeRenderingContext( CodeWriter codeWriter, IntermediateNodeWriter nodeWriter, RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode, RazorCodeGenerationOptions options) { if (codeWriter == null) { throw new ArgumentNullException(nameof(codeWriter)); } if (nodeWriter == null) { throw new ArgumentNullException(nameof(nodeWriter)); } if (codeDocument == null) { throw new ArgumentNullException(nameof(codeDocument)); } if (documentNode == null) { throw new ArgumentNullException(nameof(documentNode)); } if (options == null) { throw new ArgumentNullException(nameof(options)); } CodeWriter = codeWriter; _codeDocument = codeDocument; _documentNode = documentNode; Options = options; _ancestors = new Stack(); Diagnostics = new RazorDiagnosticCollection(); Items = new ItemCollection(); LineMappings = new List(); var diagnostics = _documentNode.GetAllDiagnostics(); for (var i = 0; i < diagnostics.Count; i++) { Diagnostics.Add(diagnostics[i]); } var newLineString = codeDocument.Items[NewLineString]; if (newLineString != null) { // Set new line character to a specific string regardless of platform, for testing purposes. codeWriter.NewLine = (string)newLineString; } Items[NewLineString] = codeDocument.Items[NewLineString]; Items[SuppressUniqueIds] = codeDocument.Items[SuppressUniqueIds]; _scopes = new List(); _scopes.Add(new ScopeInternal(nodeWriter)); } // This will be initialized by the document writer when the context is 'live'. public IntermediateNodeVisitor Visitor { get; set; } public override IEnumerable Ancestors => _ancestors; internal Stack AncestorsInternal => _ancestors; public override CodeWriter CodeWriter { get; } public override RazorDiagnosticCollection Diagnostics { get; } public override string DocumentKind { get; } public override ItemCollection Items { get; } public List LineMappings { get; } public override IntermediateNodeWriter NodeWriter => Current.Writer; public override RazorCodeGenerationOptions Options { get; } public override IntermediateNode Parent => _ancestors.Count == 0 ? null : _ancestors.Peek(); public override RazorSourceDocument SourceDocument => _codeDocument.Source; private ScopeInternal Current => _scopes[_scopes.Count - 1]; public override void AddLineMappingFor(IntermediateNode node) { if (node == null) { throw new ArgumentNullException(nameof(node)); } if (node.Source == null) { return; } if (SourceDocument.FilePath != null && !string.Equals(SourceDocument.FilePath, node.Source.Value.FilePath, StringComparison.OrdinalIgnoreCase)) { // We don't want to generate line mappings for imports. return; } var source = node.Source.Value; var generatedLocation = new SourceSpan(CodeWriter.Location, source.Length); var lineMapping = new LineMapping(source, generatedLocation); LineMappings.Add(lineMapping); } public override Scope CreateScope() { CreateScope(Current.Writer); return new Scope(this); } public override Scope CreateScope(IntermediateNodeWriter writer) { if (writer == null) { throw new ArgumentNullException(nameof(writer)); } _scopes.Add(new ScopeInternal(writer)); return new Scope(this); } public override void EndScope() { _scopes.RemoveAt(_scopes.Count - 1); } public override void RenderChildren(IntermediateNode node) { if (node == null) { throw new ArgumentNullException(nameof(node)); } _ancestors.Push(node); for (var i = 0; i < node.Children.Count; i++) { Visitor.Visit(node.Children[i]); } _ancestors.Pop(); } public override void RenderNode(IntermediateNode node) { if (node == null) { throw new ArgumentNullException(nameof(node)); } Visitor.Visit(node); } private struct ScopeInternal { public ScopeInternal(IntermediateNodeWriter writer) { Writer = writer; } public IntermediateNodeWriter Writer { get; } } } }