public void WriterConstructedWithoutContentLengthAndSourceFile_AddsLinePragmas_OnDispose() { // Arrange var location = new SourceLocation(10, 1, 20); var expected = string.Join(Environment.NewLine, @"#line 2 ""myfile""", "Hello world", "", "#line default", "#line hidden", ""); var expectedMappings = new LineMapping( new MappingLocation(location, 30), new MappingLocation(new SourceLocation(18, 1, 0), 11)); var writer = new CSharpCodeWriter(); // Act using (var mappingWriter = new CSharpLineMappingWriter(writer, location, "myfile")) { writer.Write("Hello world"); } // Assert Assert.Equal(expected, writer.GenerateCode()); Assert.Empty(writer.LineMappingManager.Mappings); }
public void CreateCodeMapping(string padding, string code, Chunk chunk) { using (CSharpLineMappingWriter mappingWriter = Writer.BuildLineMapping(chunk.Start, code.Length, Context.SourceFile)) { Writer.Write(padding); mappingWriter.MarkLineMappingStart(); Writer.Write(code); mappingWriter.MarkLineMappingEnd(); } }
private void RenderRuntimeExpressionBlockChunkWithContentSpan(ExpressionBlockChunk chunk, Span contentSpan) { var generateInstrumentation = ShouldGenerateInstrumentationForExpressions(); if (generateInstrumentation) { Writer.WriteStartInstrumentationContext(Context, contentSpan, isLiteral: false); } using (var mappingWriter = new CSharpLineMappingWriter(Writer, chunk.Start, Context.SourceFile)) { if (!string.IsNullOrEmpty(Context.TargetWriterName)) { var generatedStart = WriteToMethodName.Length + Context.TargetWriterName.Length + 3; // 1 for the opening '(' and 2 for ', ' var padding = _paddingBuilder.BuildExpressionPadding(contentSpan, generatedStart); Writer .Write(padding) .WriteStartMethodInvocation(WriteToMethodName) .Write(Context.TargetWriterName) .WriteParameterSeparator(); } else { var generatedStart = WriteMethodName.Length + 1; // for the opening '(' var padding = _paddingBuilder.BuildExpressionPadding(contentSpan, generatedStart); Writer .Write(padding) .WriteStartMethodInvocation(WriteMethodName); } Accept(chunk.Children); Writer.WriteEndMethodInvocation(); } if (generateInstrumentation) { Writer.WriteEndInstrumentationContext(Context); } }
protected override void Visit(SetBaseTypeChunk chunk) { if (Context.Host.DesignTimeMode) { using (CSharpLineMappingWriter lineMappingWriter = Writer.BuildLineMapping(chunk.Start, chunk.TypeName.Length, Context.SourceFile)) { Writer.Indent(chunk.Start.CharacterIndex); lineMappingWriter.MarkLineMappingStart(); Writer.Write(chunk.TypeName); lineMappingWriter.MarkLineMappingEnd(); Writer.Write(" ").Write(InheritsHelper).Write(" = null;"); } } }
public void WriterConstructedWithContentLength_AddsLineMappings_OnDispose() { // Arrange var location = new SourceLocation(10, 15, 20); var expected = new LineMapping( new MappingLocation(location, 30), new MappingLocation(new SourceLocation(0, 0, 0), 11)); var writer = new CSharpCodeWriter(); // Act using (var mappingWriter = new CSharpLineMappingWriter(writer, location, 30)) { writer.Write("Hello world"); } // Assert Assert.Equal("Hello world", writer.GenerateCode()); var mapping = Assert.Single(writer.LineMappingManager.Mappings); Assert.Equal(expected, mapping); }
private void RenderBoundHTMLAttributes(IDictionary <string, Chunk> chunkAttributes, string tagHelperVariableName, IEnumerable <TagHelperAttributeDescriptor> attributeDescriptors, Dictionary <string, string> htmlAttributeValues) { foreach (var attributeDescriptor in attributeDescriptors) { Chunk attributeValueChunk; var providedAttribute = chunkAttributes.TryGetValue(attributeDescriptor.Name, out attributeValueChunk); if (providedAttribute) { var attributeValueRecorded = htmlAttributeValues.ContainsKey(attributeDescriptor.Name); // Bufferable attributes are attributes that can have Razor code inside of them. var bufferableAttribute = IsStringAttribute(attributeDescriptor); // Plain text values are non Razor code (@DateTime.Now) values. If an attribute is bufferable it // may be more than just a plain text value, it may also contain Razor code which is why we attempt // to retrieve a plain text value here. string textValue; var isPlainTextValue = TryGetPlainTextValue(attributeValueChunk, out textValue); // If we haven't recorded a value and we need to buffer an attribute value and the value is not // plain text then we need to prepare the value prior to setting it below. if (!attributeValueRecorded && bufferableAttribute && !isPlainTextValue) { BuildBufferedWritingScope(attributeValueChunk); } // We capture the tag helpers property value accessor so we can retrieve it later (if we need to). var valueAccessor = string.Format(CultureInfo.InvariantCulture, "{0}.{1}", tagHelperVariableName, attributeDescriptor.PropertyName); // If we haven't recorded this attribute value before then we need to record its value. if (!attributeValueRecorded) { // We only need to create attribute values once per HTML element (not once per tag helper). // We're saving the value accessor so we can retrieve it later if there are more tag // helpers that need the value. htmlAttributeValues.Add(attributeDescriptor.Name, valueAccessor); if (bufferableAttribute) { _writer.WriteStartAssignment(valueAccessor); if (isPlainTextValue) { // If the attribute is bufferable but has a plain text value that means the value // is a string which needs to be surrounded in quotes. RenderQuotedAttributeValue(textValue, attributeDescriptor); } else { // The value contains more than plain text e.g. // stringAttribute ="Time: @DateTime.Now" RenderBufferedAttributeValue(attributeDescriptor); } _writer.WriteLine(";"); } else { // Write out simple assignment for non-string property value. Try to keep the whole // statement together and the #line pragma correct to make debugging possible. using (var lineMapper = new CSharpLineMappingWriter( _writer, attributeValueChunk.Association.Start, _context.SourceFile)) { // Place the assignment LHS to align RHS with original attribute value's indentation. // Unfortunately originalIndent is incorrect if original line contains tabs. Unable to // use a CSharpPaddingBuilder because the Association has no Previous node; lost the // original Span sequence when the parse tree was rewritten. var originalIndent = attributeValueChunk.Start.CharacterIndex; var generatedLength = valueAccessor.Length + " = ".Length; var newIndent = originalIndent - generatedLength; if (newIndent > 0) { _writer.Indent(newIndent); } _writer.WriteStartAssignment(valueAccessor); lineMapper.MarkLineMappingStart(); // Write out bare expression for this attribute value. Property is not a string. // So quoting or buffering are not helpful. RenderRawAttributeValue(attributeValueChunk, attributeDescriptor, isPlainTextValue); // End the assignment to the attribute. lineMapper.MarkLineMappingEnd(); _writer.WriteLine(";"); } } // Execution contexts are a runtime feature. if (_designTimeMode) { continue; } // We need to inform the context of the attribute value. _writer .WriteStartInstanceMethodInvocation( ExecutionContextVariableName, _tagHelperContext.ExecutionContextAddTagHelperAttributeMethodName) .WriteStringLiteral(attributeDescriptor.Name) .WriteParameterSeparator() .Write(valueAccessor) .WriteEndMethodInvocation(); } else { // The attribute value has already been recorded, lets retrieve it from the stored value // accessors. _writer .WriteStartAssignment(valueAccessor) .Write(htmlAttributeValues[attributeDescriptor.Name]) .WriteLine(";"); } } } }