Add legacy dependencies of C# lowering

This commit is contained in:
Ryan Nowak 2016-12-04 23:30:34 -08:00
parent 03549bb542
commit 9cefcdd450
10 changed files with 1480 additions and 0 deletions

View File

@ -0,0 +1,536 @@
// 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 System.Diagnostics;
using System.Globalization;
using System.Linq;
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
internal class CSharpCodeWriter : CodeWriter
{
private const string InstanceMethodFormat = "{0}.{1}";
private static readonly char[] CStyleStringLiteralEscapeChars = {
'\r',
'\t',
'\"',
'\'',
'\\',
'\0',
'\n',
'\u2028',
'\u2029',
};
public CSharpCodeWriter()
{
LineMappingManager = new LineMappingManager();
}
public LineMappingManager LineMappingManager { get; private set; }
public new CSharpCodeWriter Write(string data)
{
return (CSharpCodeWriter)base.Write(data);
}
public new CSharpCodeWriter Indent(int size)
{
return (CSharpCodeWriter)base.Indent(size);
}
public new CSharpCodeWriter ResetIndent()
{
return (CSharpCodeWriter)base.ResetIndent();
}
public new CSharpCodeWriter SetIndent(int size)
{
return (CSharpCodeWriter)base.SetIndent(size);
}
public new CSharpCodeWriter IncreaseIndent(int size)
{
return (CSharpCodeWriter)base.IncreaseIndent(size);
}
public new CSharpCodeWriter DecreaseIndent(int size)
{
return (CSharpCodeWriter)base.DecreaseIndent(size);
}
public new CSharpCodeWriter WriteLine(string data)
{
return (CSharpCodeWriter)base.WriteLine(data);
}
public new CSharpCodeWriter WriteLine()
{
return (CSharpCodeWriter)base.WriteLine();
}
public CSharpCodeWriter WriteVariableDeclaration(string type, string name, string value)
{
Write(type).Write(" ").Write(name);
if (!string.IsNullOrEmpty(value))
{
Write(" = ").Write(value);
}
else
{
Write(" = null");
}
WriteLine(";");
return this;
}
public CSharpCodeWriter WriteComment(string comment)
{
return Write("// ").WriteLine(comment);
}
public CSharpCodeWriter WriteBooleanLiteral(bool value)
{
return Write(value.ToString().ToLowerInvariant());
}
public CSharpCodeWriter WriteStartAssignment(string name)
{
return Write(name).Write(" = ");
}
public CSharpCodeWriter WriteParameterSeparator()
{
return Write(", ");
}
public CSharpCodeWriter WriteStartNewObject(string typeName)
{
return Write("new ").Write(typeName).Write("(");
}
public CSharpCodeWriter WriteLocationTaggedString(LocationTagged<string> value)
{
WriteStringLiteral(value.Value);
WriteParameterSeparator();
Write(value.Location.AbsoluteIndex.ToString(CultureInfo.InvariantCulture));
return this;
}
public CSharpCodeWriter WriteStringLiteral(string literal)
{
if (literal.Length >= 256 && literal.Length <= 1500 && literal.IndexOf('\0') == -1)
{
WriteVerbatimStringLiteral(literal);
}
else
{
WriteCStyleStringLiteral(literal);
}
return this;
}
public CSharpCodeWriter WriteLineHiddenDirective()
{
return WriteLine("#line hidden");
}
public CSharpCodeWriter WritePragma(string value)
{
return Write("#pragma ").WriteLine(value);
}
public CSharpCodeWriter WriteUsing(string name)
{
return WriteUsing(name, endLine: true);
}
public CSharpCodeWriter WriteUsing(string name, bool endLine)
{
Write("using ");
Write(name);
if (endLine)
{
WriteLine(";");
}
return this;
}
public CSharpCodeWriter WriteLineDefaultDirective()
{
return WriteLine("#line default");
}
public CSharpCodeWriter WriteStartReturn()
{
return Write("return ");
}
public CSharpCodeWriter WriteReturn(string value)
{
return WriteReturn(value, endLine: true);
}
public CSharpCodeWriter WriteReturn(string value, bool endLine)
{
Write("return ").Write(value);
if (endLine)
{
Write(";");
}
return WriteLine();
}
/// <summary>
/// Writes a <c>#line</c> pragma directive for the line number at the specified <paramref name="location"/>.
/// </summary>
/// <param name="location">The location to generate the line pragma for.</param>
/// <param name="file">The file to generate the line pragma for.</param>
/// <returns>The current instance of <see cref="CSharpCodeWriter"/>.</returns>
public CSharpCodeWriter WriteLineNumberDirective(SourceLocation location, string file)
{
if (location.FilePath != null)
{
file = location.FilePath;
}
if (Builder.Length >= NewLine.Length && !IsAfterNewLine)
{
WriteLine();
}
var lineNumberAsString = (location.LineIndex + 1).ToString(CultureInfo.InvariantCulture);
return Write("#line ").Write(lineNumberAsString).Write(" \"").Write(file).WriteLine("\"");
}
public CSharpCodeWriter WriteStartMethodInvocation(string methodName)
{
return WriteStartMethodInvocation(methodName, new string[0]);
}
public CSharpCodeWriter WriteStartMethodInvocation(string methodName, params string[] genericArguments)
{
Write(methodName);
if (genericArguments.Length > 0)
{
Write("<").Write(string.Join(", ", genericArguments)).Write(">");
}
return Write("(");
}
public CSharpCodeWriter WriteEndMethodInvocation()
{
return WriteEndMethodInvocation(endLine: true);
}
public CSharpCodeWriter WriteEndMethodInvocation(bool endLine)
{
Write(")");
if (endLine)
{
WriteLine(";");
}
return this;
}
// Writes a method invocation for the given instance name.
public CSharpCodeWriter WriteInstanceMethodInvocation(string instanceName,
string methodName,
params string[] parameters)
{
if (instanceName == null)
{
throw new ArgumentNullException(nameof(instanceName));
}
if (methodName == null)
{
throw new ArgumentNullException(nameof(methodName));
}
return WriteInstanceMethodInvocation(instanceName, methodName, endLine: true, parameters: parameters);
}
// Writes a method invocation for the given instance name.
public CSharpCodeWriter WriteInstanceMethodInvocation(string instanceName,
string methodName,
bool endLine,
params string[] parameters)
{
if (instanceName == null)
{
throw new ArgumentNullException(nameof(instanceName));
}
if (methodName == null)
{
throw new ArgumentNullException(nameof(methodName));
}
return WriteMethodInvocation(
string.Format(CultureInfo.InvariantCulture, InstanceMethodFormat, instanceName, methodName),
endLine,
parameters);
}
public CSharpCodeWriter WriteStartInstanceMethodInvocation(string instanceName,
string methodName)
{
if (instanceName == null)
{
throw new ArgumentNullException(nameof(instanceName));
}
if (methodName == null)
{
throw new ArgumentNullException(nameof(methodName));
}
return WriteStartMethodInvocation(
string.Format(CultureInfo.InvariantCulture, InstanceMethodFormat, instanceName, methodName));
}
public CSharpCodeWriter WriteMethodInvocation(string methodName, params string[] parameters)
{
return WriteMethodInvocation(methodName, endLine: true, parameters: parameters);
}
public CSharpCodeWriter WriteMethodInvocation(string methodName, bool endLine, params string[] parameters)
{
return WriteStartMethodInvocation(methodName)
.Write(string.Join(", ", parameters))
.WriteEndMethodInvocation(endLine);
}
public CSharpCodeWriter WriteAutoPropertyDeclaration(string accessibility, string typeName, string name)
{
return Write(accessibility)
.Write(" ")
.Write(typeName)
.Write(" ")
.Write(name)
.Write(" { get; set; }")
.WriteLine();
}
public CSharpDisableWarningScope BuildDisableWarningScope(int warning)
{
return new CSharpDisableWarningScope(this, warning);
}
public CSharpCodeWritingScope BuildScope()
{
return new CSharpCodeWritingScope(this);
}
public CSharpCodeWritingScope BuildLambda(bool endLine, params string[] parameterNames)
{
return BuildLambda(endLine, async: false, parameterNames: parameterNames);
}
public CSharpCodeWritingScope BuildAsyncLambda(bool endLine, params string[] parameterNames)
{
return BuildLambda(endLine, async: true, parameterNames: parameterNames);
}
private CSharpCodeWritingScope BuildLambda(bool endLine, bool async, string[] parameterNames)
{
if (async)
{
Write("async");
}
Write("(").Write(string.Join(", ", parameterNames)).Write(") => ");
var scope = new CSharpCodeWritingScope(this);
if (endLine)
{
// End the lambda with a semicolon
scope.OnClose += () =>
{
WriteLine(";");
};
}
return scope;
}
public CSharpCodeWritingScope BuildNamespace(string name)
{
Write("namespace ").WriteLine(name);
return new CSharpCodeWritingScope(this);
}
public CSharpCodeWritingScope BuildClassDeclaration(string accessibility, string name)
{
return BuildClassDeclaration(accessibility, name, Enumerable.Empty<string>());
}
public CSharpCodeWritingScope BuildClassDeclaration(string accessibility, string name, string baseType)
{
return BuildClassDeclaration(accessibility, name, new string[] { baseType });
}
public CSharpCodeWritingScope BuildClassDeclaration(
string accessibility,
string name,
IEnumerable<string> baseTypes)
{
Write(accessibility).Write(" class ").Write(name);
if (baseTypes.Count() > 0)
{
Write(" : ");
Write(string.Join(", ", baseTypes));
}
WriteLine();
return new CSharpCodeWritingScope(this);
}
public CSharpCodeWritingScope BuildConstructor(string name)
{
return BuildConstructor("public", name);
}
public CSharpCodeWritingScope BuildConstructor(string accessibility, string name)
{
return BuildConstructor(accessibility, name, Enumerable.Empty<KeyValuePair<string, string>>());
}
public CSharpCodeWritingScope BuildConstructor(
string accessibility,
string name,
IEnumerable<KeyValuePair<string, string>> parameters)
{
Write(accessibility)
.Write(" ")
.Write(name)
.Write("(")
.Write(string.Join(", ", parameters.Select(p => p.Key + " " + p.Value)))
.WriteLine(")");
return new CSharpCodeWritingScope(this);
}
public CSharpCodeWritingScope BuildMethodDeclaration(string accessibility, string returnType, string name)
{
return BuildMethodDeclaration(accessibility, returnType, name, Enumerable.Empty<KeyValuePair<string, string>>());
}
public CSharpCodeWritingScope BuildMethodDeclaration(
string accessibility,
string returnType,
string name,
IEnumerable<KeyValuePair<string, string>> parameters)
{
Write(accessibility)
.Write(" ")
.Write(returnType)
.Write(" ")
.Write(name)
.Write("(")
.Write(string.Join(", ", parameters.Select(p => p.Key + " " + p.Value)))
.WriteLine(")");
return new CSharpCodeWritingScope(this);
}
private void WriteVerbatimStringLiteral(string literal)
{
Write("@\"");
// We need to find the index of each '"' (double-quote) to escape it.
var start = 0;
int end;
while ((end = literal.IndexOf('\"', start)) > -1)
{
Write(literal, start, end - start);
Write("\"\"");
start = end + 1;
}
Debug.Assert(end == -1); // We've hit all of the double-quotes.
// Write the remainder after the last double-quote.
Write(literal, start, literal.Length - start);
Write("\"");
}
private void WriteCStyleStringLiteral(string literal)
{
// From CSharpCodeGenerator.QuoteSnippetStringCStyle in CodeDOM
Write("\"");
// We need to find the index of each escapable character to escape it.
var start = 0;
int end;
while ((end = literal.IndexOfAny(CStyleStringLiteralEscapeChars, start)) > -1)
{
Write(literal, start, end - start);
switch (literal[end])
{
case '\r':
Write("\\r");
break;
case '\t':
Write("\\t");
break;
case '\"':
Write("\\\"");
break;
case '\'':
Write("\\\'");
break;
case '\\':
Write("\\\\");
break;
case '\0':
Write("\\\0");
break;
case '\n':
Write("\\n");
break;
case '\u2028':
case '\u2029':
Write("\\u");
Write(((int)literal[end]).ToString("X4", CultureInfo.InvariantCulture));
break;
default:
Debug.Assert(false, "Unknown escape character.");
break;
}
start = end + 1;
}
Debug.Assert(end == -1); // We've hit all of chars that need escaping.
// Write the remainder after the last escaped char.
Write(literal, start, literal.Length - start);
Write("\"");
}
}
}

View File

@ -0,0 +1,71 @@
// 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.Linq;
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
internal struct CSharpCodeWritingScope : IDisposable
{
private CodeWriter _writer;
private bool _autoSpace;
private int _tabSize;
private int _startIndent;
public CSharpCodeWritingScope(CodeWriter writer) : this(writer, true) { }
public CSharpCodeWritingScope(CodeWriter writer, int tabSize) : this(writer, tabSize, true) { }
// TODO: Make indents (tabs) environment specific
public CSharpCodeWritingScope(CodeWriter writer, bool autoSpace) : this(writer, 4, autoSpace) { }
public CSharpCodeWritingScope(CodeWriter writer, int tabSize, bool autoSpace)
{
_writer = writer;
_autoSpace = true;
_tabSize = tabSize;
_startIndent = -1; // Set in WriteStartScope
OnClose = () => { };
WriteStartScope();
}
public Action OnClose;
public void Dispose()
{
WriteEndScope();
OnClose();
}
private void WriteStartScope()
{
TryAutoSpace(" ");
_writer.WriteLine("{").IncreaseIndent(_tabSize);
_startIndent = _writer.CurrentIndent;
}
private void WriteEndScope()
{
TryAutoSpace(_writer.NewLine);
// Ensure the scope hasn't been modified
if (_writer.CurrentIndent == _startIndent)
{
_writer.DecreaseIndent(_tabSize);
}
_writer.WriteLine("}");
}
private void TryAutoSpace(string spaceCharacter)
{
if (_autoSpace &&
_writer.Builder.Length > 0 &&
!char.IsWhiteSpace(_writer.Builder[_writer.Builder.Length - 1]))
{
_writer.Write(spaceCharacter);
}
}
}
}

View File

@ -0,0 +1,29 @@
// 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;
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
internal struct CSharpDisableWarningScope : IDisposable
{
private CSharpCodeWriter _writer;
int _warningNumber;
public CSharpDisableWarningScope(CSharpCodeWriter writer) : this(writer, 219)
{ }
public CSharpDisableWarningScope(CSharpCodeWriter writer, int warningNumber)
{
_writer = writer;
_warningNumber = warningNumber;
_writer.WritePragma("warning disable " + _warningNumber);
}
public void Dispose()
{
_writer.WritePragma("warning restore " + _warningNumber);
}
}
}

View File

@ -0,0 +1,203 @@
// 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.Text;
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
internal class CodeWriter : IDisposable
{
private static readonly char[] NewLineCharacters = { '\r', '\n' };
private string _cache = string.Empty;
private bool _dirty;
private int _absoluteIndex;
private int _currentLineIndex;
private int _currentLineCharacterIndex;
public StringBuilder Builder { get; } = new StringBuilder();
public int CurrentIndent { get; private set; }
public bool IsAfterNewLine { get; private set; }
public string NewLine { get; set; } = Environment.NewLine;
public CodeWriter ResetIndent()
{
return SetIndent(0);
}
public CodeWriter IncreaseIndent(int size)
{
CurrentIndent += size;
return this;
}
public CodeWriter DecreaseIndent(int size)
{
CurrentIndent -= size;
return this;
}
public CodeWriter SetIndent(int size)
{
CurrentIndent = size;
return this;
}
public CodeWriter Indent(int size)
{
if (IsAfterNewLine)
{
Builder.Append(' ', size);
_currentLineCharacterIndex += size;
_absoluteIndex += size;
_dirty = true;
IsAfterNewLine = false;
}
return this;
}
public CodeWriter Write(string data)
{
if (data == null)
{
return this;
}
return Write(data, 0, data.Length);
}
public CodeWriter Write(string data, int index, int count)
{
if (data == null || count == 0)
{
return this;
}
Indent(CurrentIndent);
Builder.Append(data, index, count);
_dirty = true;
IsAfterNewLine = false;
_absoluteIndex += count;
// The data string might contain a partial newline where the previously
// written string has part of the newline.
var i = index;
int? trailingPartStart = null;
if (
// Check the last character of the previous write operation.
Builder.Length - count - 1 >= 0 &&
Builder[Builder.Length - count - 1] == '\r' &&
// Check the first character of the current write operation.
Builder[Builder.Length - count] == '\n')
{
// This is newline that's spread across two writes. Skip the first character of the
// current write operation.
//
// We don't need to increment our newline counter because we already did that when we
// saw the \r.
i += 1;
trailingPartStart = 1;
}
// Iterate the string, stopping at each occurrence of a newline character. This lets us count the
// newline occurrences and keep the index of the last one.
while ((i = data.IndexOfAny(NewLineCharacters, i)) >= 0)
{
// Newline found.
_currentLineIndex++;
_currentLineCharacterIndex = 0;
i++;
// We might have stopped at a \r, so check if it's followed by \n and then advance the index to
// start the next search after it.
if (count > i &&
data[i - 1] == '\r' &&
data[i] == '\n')
{
i++;
}
// The 'suffix' of the current line starts after this newline token.
trailingPartStart = i;
}
if (trailingPartStart == null)
{
// No newlines, just add the length of the data buffer
_currentLineCharacterIndex += count;
}
else
{
// Newlines found, add the trailing part of 'data'
_currentLineCharacterIndex += (count - trailingPartStart.Value);
}
return this;
}
public CodeWriter WriteLine()
{
Builder.Append(NewLine);
_currentLineIndex++;
_currentLineCharacterIndex = 0;
_absoluteIndex += NewLine.Length;
_dirty = true;
IsAfterNewLine = true;
return this;
}
public CodeWriter WriteLine(string data)
{
return Write(data).WriteLine();
}
public string GenerateCode()
{
if (_dirty)
{
_cache = Builder.ToString();
_dirty = false;
}
return _cache;
}
public SourceLocation GetCurrentSourceLocation()
{
return new SourceLocation(_absoluteIndex, _currentLineIndex, _currentLineCharacterIndex);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
Builder.Clear();
}
}
public void Dispose()
{
Dispose(disposing: true);
}
}
}

View File

@ -0,0 +1,79 @@
// 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.Globalization;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
internal class LineMapping
{
public LineMapping(MappingLocation documentLocation, MappingLocation generatedLocation)
{
DocumentLocation = documentLocation;
GeneratedLocation = generatedLocation;
}
public MappingLocation DocumentLocation { get; }
public MappingLocation GeneratedLocation { get; }
public override bool Equals(object obj)
{
var other = obj as LineMapping;
if (ReferenceEquals(other, null))
{
return false;
}
return DocumentLocation.Equals(other.DocumentLocation) &&
GeneratedLocation.Equals(other.GeneratedLocation);
}
public override int GetHashCode()
{
var hashCodeCombiner = HashCodeCombiner.Start();
hashCodeCombiner.Add(DocumentLocation);
hashCodeCombiner.Add(GeneratedLocation);
return hashCodeCombiner;
}
public static bool operator ==(LineMapping left, LineMapping right)
{
if (ReferenceEquals(left, right))
{
// Exact equality e.g. both objects are null.
return true;
}
if (ReferenceEquals(left, null))
{
return false;
}
return left.Equals(right);
}
public static bool operator !=(LineMapping left, LineMapping right)
{
if (ReferenceEquals(left, right))
{
// Exact equality e.g. both objects are null.
return false;
}
if (ReferenceEquals(left, null))
{
return true;
}
return !left.Equals(right);
}
public override string ToString()
{
return string.Format(CultureInfo.CurrentCulture, "{0} -> {1}", DocumentLocation, GeneratedLocation);
}
}
}

View File

@ -0,0 +1,22 @@
// 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.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
internal class LineMappingManager
{
public LineMappingManager()
{
Mappings = new List<LineMapping>();
}
public List<LineMapping> Mappings { get; }
public void AddMapping(MappingLocation documentLocation, MappingLocation generatedLocation)
{
Mappings.Add(new LineMapping(documentLocation, generatedLocation));
}
}
}

View File

@ -0,0 +1,105 @@
// 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.Globalization;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
internal class MappingLocation
{
public MappingLocation()
{
}
public MappingLocation(SourceLocation location, int contentLength)
{
ContentLength = contentLength;
AbsoluteIndex = location.AbsoluteIndex;
LineIndex = location.LineIndex;
CharacterIndex = location.CharacterIndex;
FilePath = location.FilePath;
}
public int ContentLength { get; }
public int AbsoluteIndex { get; }
public int LineIndex { get; }
public int CharacterIndex { get; }
public string FilePath { get; }
public override bool Equals(object obj)
{
var other = obj as MappingLocation;
if (ReferenceEquals(other, null))
{
return false;
}
return string.Equals(FilePath, other.FilePath, StringComparison.Ordinal) &&
AbsoluteIndex == other.AbsoluteIndex &&
ContentLength == other.ContentLength &&
LineIndex == other.LineIndex &&
CharacterIndex == other.CharacterIndex;
}
public override int GetHashCode()
{
var hashCodeCombiner = HashCodeCombiner.Start();
hashCodeCombiner.Add(FilePath, StringComparer.Ordinal);
hashCodeCombiner.Add(AbsoluteIndex);
hashCodeCombiner.Add(ContentLength);
hashCodeCombiner.Add(LineIndex);
hashCodeCombiner.Add(CharacterIndex);
return hashCodeCombiner;
}
public override string ToString()
{
return string.Format(
CultureInfo.CurrentCulture, "({0}:{1},{2} [{3}] {4})",
AbsoluteIndex,
LineIndex,
CharacterIndex,
ContentLength,
FilePath);
}
public static bool operator ==(MappingLocation left, MappingLocation right)
{
if (ReferenceEquals(left, right))
{
// Exact equality e.g. both objects are null.
return true;
}
if (ReferenceEquals(left, null))
{
return false;
}
return left.Equals(right);
}
public static bool operator !=(MappingLocation left, MappingLocation right)
{
if (ReferenceEquals(left, right))
{
// Exact equality e.g. both objects are null.
return false;
}
if (ReferenceEquals(left, null))
{
return true;
}
return !left.Equals(right);
}
}
}

View File

@ -0,0 +1,47 @@
// 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 Xunit;
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
public class CSharpCodeWriterTest
{
[Fact]
public void WriteLineNumberDirective_UsesFilePath_WhenFileInSourceLocationIsNull()
{
// Arrange
var filePath = "some-path";
var writer = new CSharpCodeWriter();
var expected = $"#line 5 \"{filePath}\"" + writer.NewLine;
var sourceLocation = new SourceLocation(10, 4, 3);
// Act
writer.WriteLineNumberDirective(sourceLocation, filePath);
var code = writer.GenerateCode();
// Assert
Assert.Equal(expected, code);
}
[Theory]
[InlineData("")]
[InlineData("source-location-file-path")]
public void WriteLineNumberDirective_UsesSourceLocationFilePath_IfAvailable(
string sourceLocationFilePath)
{
// Arrange
var filePath = "some-path";
var writer = new CSharpCodeWriter();
var expected = $"#line 5 \"{sourceLocationFilePath}\"" + writer.NewLine;
var sourceLocation = new SourceLocation(sourceLocationFilePath, 10, 4, 3);
// Act
writer.WriteLineNumberDirective(sourceLocation, filePath);
var code = writer.GenerateCode();
// Assert
Assert.Equal(expected, code);
}
}
}

View File

@ -0,0 +1,265 @@
// 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 Xunit;
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
public class CodeWriterTest
{
// The length of the newline string written by writer.WriteLine.
private static readonly int WriterNewLineLength = Environment.NewLine.Length;
public static IEnumerable<object[]> NewLines
{
get
{
return new object[][]
{
new object[] { "\r" },
new object[] { "\n" },
new object[] { "\r\n" },
};
}
}
[Fact]
public void CodeWriter_TracksPosition_WithWrite()
{
// Arrange
var writer = new CodeWriter();
// Act
writer.Write("1234");
// Assert
var location = writer.GetCurrentSourceLocation();
var expected = new SourceLocation(absoluteIndex: 4, lineIndex: 0, characterIndex: 4);
Assert.Equal(expected, location);
}
[Fact]
public void CodeWriter_TracksPosition_WithIndent()
{
// Arrange
var writer = new CodeWriter();
// Act
writer.WriteLine();
writer.Indent(size: 3);
// Assert
var location = writer.GetCurrentSourceLocation();
var expected = new SourceLocation(absoluteIndex: 3 + WriterNewLineLength, lineIndex: 1, characterIndex: 3);
Assert.Equal(expected, location);
}
[Fact]
public void CodeWriter_TracksPosition_WithWriteLine()
{
// Arrange
var writer = new CodeWriter();
// Act
writer.WriteLine("1234");
// Assert
var location = writer.GetCurrentSourceLocation();
var expected = new SourceLocation(absoluteIndex: 4 + WriterNewLineLength, lineIndex: 1, characterIndex: 0);
Assert.Equal(expected, location);
}
[Theory]
[MemberData("NewLines")]
public void CodeWriter_TracksPosition_WithWriteLine_WithNewLineInContent(string newLine)
{
// Arrange
var writer = new CodeWriter();
// Act
writer.WriteLine("1234" + newLine + "12");
// Assert
var location = writer.GetCurrentSourceLocation();
var expected = new SourceLocation(
absoluteIndex: 6 + newLine.Length + WriterNewLineLength,
lineIndex: 2,
characterIndex: 0);
Assert.Equal(expected, location);
}
[Theory]
[MemberData("NewLines")]
public void CodeWriter_TracksPosition_WithWrite_WithNewlineInContent(string newLine)
{
// Arrange
var writer = new CodeWriter();
// Act
writer.Write("1234" + newLine + "123" + newLine + "12");
// Assert
var location = writer.GetCurrentSourceLocation();
var expected = new SourceLocation(
absoluteIndex: 9 + newLine.Length + newLine.Length,
lineIndex: 2,
characterIndex: 2);
Assert.Equal(expected, location);
}
[Fact]
public void CodeWriter_TracksPosition_WithWrite_WithNewlineInContent_RepeatedN()
{
// Arrange
var writer = new CodeWriter();
// Act
writer.Write("1234\n\n123");
// Assert
var location = writer.GetCurrentSourceLocation();
var expected = new SourceLocation(
absoluteIndex: 9,
lineIndex: 2,
characterIndex: 3);
Assert.Equal(expected, location);
}
[Fact]
public void CodeWriter_TracksPosition_WithWrite_WithMixedNewlineInContent()
{
// Arrange
var writer = new CodeWriter();
// Act
writer.Write("1234\r123\r\n12\n1");
// Assert
var location = writer.GetCurrentSourceLocation();
var expected = new SourceLocation(
absoluteIndex: 14,
lineIndex: 3,
characterIndex: 1);
Assert.Equal(expected, location);
}
[Fact]
public void CodeWriter_TracksPosition_WithNewline_SplitAcrossWrites()
{
// Arrange
var writer = new CodeWriter();
// Act
writer.Write("1234\r");
var location1 = writer.GetCurrentSourceLocation();
writer.Write("\n");
var location2 = writer.GetCurrentSourceLocation();
// Assert
var expected1 = new SourceLocation(absoluteIndex: 5, lineIndex: 1, characterIndex: 0);
Assert.Equal(expected1, location1);
var expected2 = new SourceLocation(absoluteIndex: 6, lineIndex: 1, characterIndex: 0);
Assert.Equal(expected2, location2);
}
[Fact]
public void CodeWriter_TracksPosition_WithTwoNewline_SplitAcrossWrites_R()
{
// Arrange
var writer = new CodeWriter();
// Act
writer.Write("1234\r");
var location1 = writer.GetCurrentSourceLocation();
writer.Write("\r");
var location2 = writer.GetCurrentSourceLocation();
// Assert
var expected1 = new SourceLocation(absoluteIndex: 5, lineIndex: 1, characterIndex: 0);
Assert.Equal(expected1, location1);
var expected2 = new SourceLocation(absoluteIndex: 6, lineIndex: 2, characterIndex: 0);
Assert.Equal(expected2, location2);
}
[Fact]
public void CodeWriter_TracksPosition_WithTwoNewline_SplitAcrossWrites_N()
{
// Arrange
var writer = new CodeWriter();
// Act
writer.Write("1234\n");
var location1 = writer.GetCurrentSourceLocation();
writer.Write("\n");
var location2 = writer.GetCurrentSourceLocation();
// Assert
var expected1 = new SourceLocation(absoluteIndex: 5, lineIndex: 1, characterIndex: 0);
Assert.Equal(expected1, location1);
var expected2 = new SourceLocation(absoluteIndex: 6, lineIndex: 2, characterIndex: 0);
Assert.Equal(expected2, location2);
}
[Fact]
public void CodeWriter_TracksPosition_WithTwoNewline_SplitAcrossWrites_Reversed()
{
// Arrange
var writer = new CodeWriter();
// Act
writer.Write("1234\n");
var location1 = writer.GetCurrentSourceLocation();
writer.Write("\r");
var location2 = writer.GetCurrentSourceLocation();
// Assert
var expected1 = new SourceLocation(absoluteIndex: 5, lineIndex: 1, characterIndex: 0);
Assert.Equal(expected1, location1);
var expected2 = new SourceLocation(absoluteIndex: 6, lineIndex: 2, characterIndex: 0);
Assert.Equal(expected2, location2);
}
[Fact]
public void CodeWriter_TracksPosition_WithNewline_SplitAcrossWrites_AtBeginning()
{
// Arrange
var writer = new CodeWriter();
// Act
writer.Write("\r");
var location1 = writer.GetCurrentSourceLocation();
writer.Write("\n");
var location2 = writer.GetCurrentSourceLocation();
// Assert
var expected1 = new SourceLocation(absoluteIndex: 1, lineIndex: 1, characterIndex: 0);
Assert.Equal(expected1, location1);
var expected2 = new SourceLocation(absoluteIndex: 2, lineIndex: 1, characterIndex: 0);
Assert.Equal(expected2, location2);
}
}
}

View File

@ -0,0 +1,123 @@
// 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 Xunit;
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
public class LineMappingTest
{
[Fact]
public void GeneratedCodeMappingsAreEqualIfDataIsEqual()
{
// Arrange
var left = new LineMapping(
new MappingLocation(new SourceLocation(1, 2, 3), 4),
new MappingLocation(new SourceLocation(5, 6, 7), 8)
);
var right = new LineMapping(
new MappingLocation(new SourceLocation(1, 2, 3), 4),
new MappingLocation(new SourceLocation(5, 6, 7), 8)
);
// Assert
Assert.True(left == right);
Assert.True(left.Equals(right));
Assert.True(right.Equals(left));
Assert.True(Equals(left, right));
}
[Fact]
public void GeneratedCodeMappingsAreNotEqualIfCodeLengthIsNotEqual()
{
// Arrange
var left = new LineMapping(
new MappingLocation(new SourceLocation(1, 2, 3), 4),
new MappingLocation(new SourceLocation(5, 6, 7), 8)
);
var right = new LineMapping(
new MappingLocation(new SourceLocation(1, 2, 3), 5),
new MappingLocation(new SourceLocation(5, 6, 7), 9)
);
// Assert
AssertNotEqual(left, right);
}
[Fact]
public void GeneratedCodeMappingsAreNotEqualIfStartGeneratedColumnIsNotEqual()
{
// Arrange
var left = new LineMapping(
new MappingLocation(new SourceLocation(1, 2, 3), 4),
new MappingLocation(new SourceLocation(5, 6, 7), 8)
);
var right = new LineMapping(
new MappingLocation(new SourceLocation(1, 2, 3), 4),
new MappingLocation(new SourceLocation(5, 6, 8), 8)
);
// Assert
AssertNotEqual(left, right);
}
[Fact]
public void GeneratedCodeMappingsAreNotEqualIfStartColumnIsNotEqual()
{
// Arrange
var left = new LineMapping(
new MappingLocation(new SourceLocation(1, 2, 3), 4),
new MappingLocation(new SourceLocation(5, 6, 8), 8)
);
var right = new LineMapping(
new MappingLocation(new SourceLocation(1, 2, 3), 4),
new MappingLocation(new SourceLocation(5, 6, 7), 8)
);
// Assert
AssertNotEqual(left, right);
}
[Fact]
public void GeneratedCodeMappingsAreNotEqualIfStartLineIsNotEqual()
{
// Arrange
var left = new LineMapping(
new MappingLocation(new SourceLocation(1, 2, 3), 4),
new MappingLocation(new SourceLocation(5, 5, 7), 8)
);
var right = new LineMapping(
new MappingLocation(new SourceLocation(1, 1, 3), 4),
new MappingLocation(new SourceLocation(5, 6, 7), 8)
);
// Assert
AssertNotEqual(left, right);
}
[Fact]
public void GeneratedCodeMappingsAreNotEqualIfAbsoluteIndexIsNotEqual()
{
// Arrange
var left = new LineMapping(
new MappingLocation(new SourceLocation(1, 2, 3), 4),
new MappingLocation(new SourceLocation(4, 6, 7), 8)
);
var right = new LineMapping(
new MappingLocation(new SourceLocation(1, 2, 3), 4),
new MappingLocation(new SourceLocation(5, 6, 7), 9)
);
// Assert
AssertNotEqual(left, right);
}
private void AssertNotEqual(LineMapping left, LineMapping right)
{
Assert.False(left == right);
Assert.False(left.Equals(right));
Assert.False(right.Equals(left));
Assert.False(Equals(left, right));
}
}
}