Improve `GetHashCode()` implementations and their consistency with `Equals()`

- #362 and more
- make a few more properties immutable
 - in a few cases, just remove `private` setters
 - in others, adjust consuming code to handle the changes
- make `Equals()` commutative
 - use `GetType() == obj.GetType()` if necessary
- use only immutable values in `GetHashCode()` calculations
 - avoid `object.GetHashCode()`; that calculates hash of reference
 - add warnings about `RazorError` property setters but use properties
 - BUT lack of immutable values leads to some static `GetHashCode()` calculations
- correct important typo in `TagHelperDescriptorComparer`
- ensure `Equals()` does not `throw` an NRE e.g. in `LineMapping`
- add `SyntaxTreeNode.GetEquivalenceHash()`
- make `SourceLocation.Equals()` and `SourceLocation.CompareTo()` consistent

Update affected tests
- code generators and edit handlers less likely to be equal; adjust expectations

Add lots of tests
- not for all updated classes but enough to see impact of odd choices

nits:
- remove some `Equals()` and `GetHashCode()` overrides in `SpanCodeGenerator` subclasses
 - no longer unique
- remove redundant null checks e.g. when also done in `base.Equals()`
- add `StringComparer.Ordinal` if `StringComparison.Ordinal` used in `Equals()`
- make some `CSharpLineMappingWriter` fields `readonly`
- remove unused `LineMapping` constructor
This commit is contained in:
Doug Bunting 2015-04-26 22:45:40 -07:00
parent 1111405786
commit 6384977e96
62 changed files with 1837 additions and 405 deletions

View File

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using Microsoft.Internal.Web.Utils;
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{ {

View File

@ -13,19 +13,28 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
{ {
public class AutoCompleteEditHandler : SpanEditHandler public class AutoCompleteEditHandler : SpanEditHandler
{ {
private static readonly int TypeHashCode = typeof(AutoCompleteEditHandler).GetHashCode();
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func<T> is the recommended delegate type and requires this level of nesting.")] [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func<T> is the recommended delegate type and requires this level of nesting.")]
public AutoCompleteEditHandler(Func<string, IEnumerable<ISymbol>> tokenizer) public AutoCompleteEditHandler(Func<string, IEnumerable<ISymbol>> tokenizer)
: base(tokenizer) : base(tokenizer)
{ {
} }
public AutoCompleteEditHandler(Func<string, IEnumerable<ISymbol>> tokenizer, bool autoCompleteAtEndOfSpan)
: this(tokenizer)
{
AutoCompleteAtEndOfSpan = autoCompleteAtEndOfSpan;
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func<T> is the recommended delegate type and requires this level of nesting.")] [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func<T> is the recommended delegate type and requires this level of nesting.")]
public AutoCompleteEditHandler(Func<string, IEnumerable<ISymbol>> tokenizer, AcceptedCharacters accepted) public AutoCompleteEditHandler(Func<string, IEnumerable<ISymbol>> tokenizer, AcceptedCharacters accepted)
: base(tokenizer, accepted) : base(tokenizer, accepted)
{ {
} }
public bool AutoCompleteAtEndOfSpan { get; set; } public bool AutoCompleteAtEndOfSpan { get; }
public string AutoCompleteString { get; set; } public string AutoCompleteString { get; set; }
protected override PartialParseResult CanAcceptChange(Span target, TextChange normalizedChange) protected override PartialParseResult CanAcceptChange(Span target, TextChange normalizedChange)
@ -48,17 +57,17 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
var other = obj as AutoCompleteEditHandler; var other = obj as AutoCompleteEditHandler;
return base.Equals(obj) && return base.Equals(other) &&
other != null && string.Equals(other.AutoCompleteString, AutoCompleteString, StringComparison.Ordinal) &&
string.Equals(other.AutoCompleteString, AutoCompleteString, StringComparison.Ordinal) && AutoCompleteAtEndOfSpan == other.AutoCompleteAtEndOfSpan;
AutoCompleteAtEndOfSpan == other.AutoCompleteAtEndOfSpan;
} }
public override int GetHashCode() public override int GetHashCode()
{ {
// Hash code should include only immutable properties but Equals also checks the type.
return HashCodeCombiner.Start() return HashCodeCombiner.Start()
.Add(base.GetHashCode()) .Add(TypeHashCode)
.Add(AutoCompleteString) .Add(AutoCompleteAtEndOfSpan)
.CombinedHash; .CombinedHash;
} }
} }

View File

@ -18,15 +18,31 @@ namespace Microsoft.AspNet.Razor.Editor
{ {
public class ImplicitExpressionEditHandler : SpanEditHandler public class ImplicitExpressionEditHandler : SpanEditHandler
{ {
private readonly ISet<string> _keywords;
private readonly IReadOnlyCollection<string> _readOnlyKeywords;
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func<T> is the recommended delegate type and requires this level of nesting.")] [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func<T> is the recommended delegate type and requires this level of nesting.")]
public ImplicitExpressionEditHandler(Func<string, IEnumerable<ISymbol>> tokenizer, ISet<string> keywords, bool acceptTrailingDot) public ImplicitExpressionEditHandler(Func<string, IEnumerable<ISymbol>> tokenizer, ISet<string> keywords, bool acceptTrailingDot)
: base(tokenizer) : base(tokenizer)
{ {
Initialize(keywords, acceptTrailingDot); _keywords = keywords ?? new HashSet<string>();
// HashSet<T> implements IReadOnlyCollection<T> as of 4.6, but does not for 4.5.1. If the runtime cast
// succeeds, avoid creating a new collection.
_readOnlyKeywords = (_keywords as IReadOnlyCollection<string>) ?? _keywords.ToArray();
AcceptTrailingDot = acceptTrailingDot;
} }
public bool AcceptTrailingDot { get; private set; } public bool AcceptTrailingDot { get; }
public ISet<string> Keywords { get; private set; }
public IReadOnlyCollection<string> Keywords
{
get
{
return _readOnlyKeywords;
}
}
public override string ToString() public override string ToString()
{ {
@ -36,18 +52,17 @@ namespace Microsoft.AspNet.Razor.Editor
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
var other = obj as ImplicitExpressionEditHandler; var other = obj as ImplicitExpressionEditHandler;
return other != null && return base.Equals(other) &&
base.Equals(other) && _keywords.SetEquals(other._keywords) &&
Keywords.SetEquals(other.Keywords) && AcceptTrailingDot == other.AcceptTrailingDot;
AcceptTrailingDot == other.AcceptTrailingDot;
} }
public override int GetHashCode() public override int GetHashCode()
{ {
// Hash code should include only immutable properties and base has none.
return HashCodeCombiner.Start() return HashCodeCombiner.Start()
.Add(base.GetHashCode())
.Add(AcceptTrailingDot)
.Add(Keywords) .Add(Keywords)
.Add(AcceptTrailingDot)
.CombinedHash; .CombinedHash;
} }
@ -104,12 +119,6 @@ namespace Microsoft.AspNet.Razor.Editor
return PartialParseResult.Rejected; return PartialParseResult.Rejected;
} }
private void Initialize(ISet<string> keywords, bool acceptTrailingDot)
{
Keywords = keywords ?? new HashSet<string>();
AcceptTrailingDot = acceptTrailingDot;
}
// A dotless commit is the process of inserting a '.' with an intellisense selection. // A dotless commit is the process of inserting a '.' with an intellisense selection.
private static bool IsDotlessCommitInsertion(Span target, TextChange change) private static bool IsDotlessCommitInsertion(Span target, TextChange change)
{ {
@ -310,7 +319,7 @@ namespace Microsoft.AspNet.Razor.Editor
{ {
using (var reader = new StringReader(newContent)) using (var reader = new StringReader(newContent))
{ {
return Keywords.Contains(reader.ReadWhile(ParserHelpers.IsIdentifierPart)); return _keywords.Contains(reader.ReadWhile(ParserHelpers.IsIdentifierPart));
} }
} }
} }

View File

@ -8,13 +8,14 @@ using System.Linq;
using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.Parser.SyntaxTree;
using Microsoft.AspNet.Razor.Text; using Microsoft.AspNet.Razor.Text;
using Microsoft.AspNet.Razor.Tokenizer.Symbols; using Microsoft.AspNet.Razor.Tokenizer.Symbols;
using Microsoft.Internal.Web.Utils;
namespace Microsoft.AspNet.Razor.Editor namespace Microsoft.AspNet.Razor.Editor
{ {
// Manages edits to a span // Manages edits to a span
public class SpanEditHandler public class SpanEditHandler
{ {
private static readonly int TypeHashCode = typeof(SpanEditHandler).GetHashCode();
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func<T> is the recommended delegate type and requires this level of nesting.")] [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func<T> is the recommended delegate type and requires this level of nesting.")]
public SpanEditHandler(Func<string, IEnumerable<ISymbol>> tokenizer) public SpanEditHandler(Func<string, IEnumerable<ISymbol>> tokenizer)
: this(tokenizer, AcceptedCharacters.Any) : this(tokenizer, AcceptedCharacters.Any)
@ -170,16 +171,15 @@ namespace Microsoft.AspNet.Razor.Editor
{ {
var other = obj as SpanEditHandler; var other = obj as SpanEditHandler;
return other != null && return other != null &&
AcceptedCharacters == other.AcceptedCharacters && GetType() == other.GetType() &&
EditorHints == other.EditorHints; AcceptedCharacters == other.AcceptedCharacters &&
EditorHints == other.EditorHints;
} }
public override int GetHashCode() public override int GetHashCode()
{ {
return HashCodeCombiner.Start() // Hash code should include only immutable properties but Equals also checks the type.
.Add(AcceptedCharacters) return TypeHashCode;
.Add(EditorHints)
.CombinedHash;
} }
} }
} }

View File

@ -3,7 +3,6 @@
using System; using System;
using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.Parser.SyntaxTree;
using Microsoft.Internal.Web.Utils;
namespace Microsoft.AspNet.Razor.Generator namespace Microsoft.AspNet.Razor.Generator
{ {
@ -15,7 +14,8 @@ namespace Microsoft.AspNet.Razor.Generator
NamespaceKeywordLength = namespaceKeywordLength; NamespaceKeywordLength = namespaceKeywordLength;
} }
public string Namespace { get; private set; } public string Namespace { get; }
public int NamespaceKeywordLength { get; set; } public int NamespaceKeywordLength { get; set; }
public override void GenerateCode(Span target, CodeGeneratorContext context) public override void GenerateCode(Span target, CodeGeneratorContext context)
@ -39,16 +39,14 @@ namespace Microsoft.AspNet.Razor.Generator
{ {
var other = obj as AddImportCodeGenerator; var other = obj as AddImportCodeGenerator;
return other != null && return other != null &&
string.Equals(Namespace, other.Namespace, StringComparison.Ordinal) && string.Equals(Namespace, other.Namespace, StringComparison.Ordinal) &&
NamespaceKeywordLength == other.NamespaceKeywordLength; NamespaceKeywordLength == other.NamespaceKeywordLength;
} }
public override int GetHashCode() public override int GetHashCode()
{ {
return HashCodeCombiner.Start() // Hash code should include only immutable properties.
.Add(Namespace) return Namespace == null ? 0 : StringComparer.Ordinal.GetHashCode(Namespace);
.Add(NamespaceKeywordLength)
.CombinedHash;
} }
} }
} }

View File

@ -19,9 +19,11 @@ namespace Microsoft.AspNet.Razor.Generator
Suffix = suffix; Suffix = suffix;
} }
public string Name { get; private set; } public string Name { get; }
public LocationTagged<string> Prefix { get; private set; }
public LocationTagged<string> Suffix { get; private set; } public LocationTagged<string> Prefix { get; }
public LocationTagged<string> Suffix { get; }
public override void GenerateStartBlockCode(Block target, CodeGeneratorContext context) public override void GenerateStartBlockCode(Block target, CodeGeneratorContext context)
{ {
@ -46,15 +48,15 @@ namespace Microsoft.AspNet.Razor.Generator
{ {
var other = obj as AttributeBlockCodeGenerator; var other = obj as AttributeBlockCodeGenerator;
return other != null && return other != null &&
string.Equals(other.Name, Name, StringComparison.Ordinal) && string.Equals(other.Name, Name, StringComparison.Ordinal) &&
Equals(other.Prefix, Prefix) && Equals(other.Prefix, Prefix) &&
Equals(other.Suffix, Suffix); Equals(other.Suffix, Suffix);
} }
public override int GetHashCode() public override int GetHashCode()
{ {
return HashCodeCombiner.Start() return HashCodeCombiner.Start()
.Add(Name) .Add(Name, StringComparer.Ordinal)
.Add(Prefix) .Add(Prefix)
.Add(Suffix) .Add(Suffix)
.CombinedHash; .CombinedHash;

View File

@ -8,6 +8,8 @@ namespace Microsoft.AspNet.Razor.Generator
{ {
public abstract class BlockCodeGenerator : IBlockCodeGenerator public abstract class BlockCodeGenerator : IBlockCodeGenerator
{ {
private static readonly int TypeHashCode = typeof(BlockCodeGenerator).GetHashCode();
[SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", Justification = "This class has no instance state")] [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", Justification = "This class has no instance state")]
public static readonly IBlockCodeGenerator Null = new NullBlockCodeGenerator(); public static readonly IBlockCodeGenerator Null = new NullBlockCodeGenerator();
@ -21,12 +23,13 @@ namespace Microsoft.AspNet.Razor.Generator
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return (obj as IBlockCodeGenerator) != null; return obj != null &&
GetType() == obj.GetType();
} }
public override int GetHashCode() public override int GetHashCode()
{ {
return base.GetHashCode(); return TypeHashCode;
} }
private class NullBlockCodeGenerator : IBlockCodeGenerator private class NullBlockCodeGenerator : IBlockCodeGenerator

View File

@ -8,16 +8,16 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
{ {
public class CSharpLineMappingWriter : IDisposable public class CSharpLineMappingWriter : IDisposable
{ {
private CSharpCodeWriter _writer; private readonly CSharpCodeWriter _writer;
private MappingLocation _documentMapping; private readonly MappingLocation _documentMapping;
private SourceLocation _generatedLocation; private readonly int _startIndent;
private int _startIndent; private readonly bool _writePragmas;
private int _generatedContentLength; private readonly bool _addLineMapping;
private bool _writePragmas;
private bool _addLineMapping;
private CSharpLineMappingWriter([NotNull] CSharpCodeWriter writer, private SourceLocation _generatedLocation;
bool addLineMappings) private int _generatedContentLength;
private CSharpLineMappingWriter([NotNull] CSharpCodeWriter writer, bool addLineMappings)
{ {
_writer = writer; _writer = writer;
_addLineMapping = addLineMappings; _addLineMapping = addLineMappings;
@ -32,7 +32,11 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
_generatedLocation = _writer.GetCurrentSourceLocation(); _generatedLocation = _writer.GetCurrentSourceLocation();
} }
public CSharpLineMappingWriter(CSharpCodeWriter writer, SourceLocation documentLocation, int contentLength, string sourceFilename) public CSharpLineMappingWriter(
CSharpCodeWriter writer,
SourceLocation documentLocation,
int contentLength,
string sourceFilename)
: this(writer, documentLocation, contentLength) : this(writer, documentLocation, contentLength)
{ {
_writePragmas = true; _writePragmas = true;
@ -49,9 +53,10 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
/// <param name="writer">The <see cref="CSharpCodeWriter"/> to write output to.</param> /// <param name="writer">The <see cref="CSharpCodeWriter"/> to write output to.</param>
/// <param name="documentLocation">The <see cref="SourceLocation"/> of the Razor content being mapping.</param> /// <param name="documentLocation">The <see cref="SourceLocation"/> of the Razor content being mapping.</param>
/// <param name="sourceFileName">The input file path.</param> /// <param name="sourceFileName">The input file path.</param>
public CSharpLineMappingWriter([NotNull] CSharpCodeWriter writer, public CSharpLineMappingWriter(
[NotNull] SourceLocation documentLocation, [NotNull] CSharpCodeWriter writer,
[NotNull] string sourceFileName) [NotNull] SourceLocation documentLocation,
[NotNull] string sourceFileName)
: this(writer, addLineMappings: false) : this(writer, addLineMappings: false)
{ {
_writePragmas = true; _writePragmas = true;
@ -79,14 +84,20 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
} }
var generatedLocation = new MappingLocation(_generatedLocation, _generatedContentLength); var generatedLocation = new MappingLocation(_generatedLocation, _generatedContentLength);
if (_documentMapping.ContentLength == -1) var documentMapping = _documentMapping;
if (documentMapping.ContentLength == -1)
{ {
_documentMapping.ContentLength = generatedLocation.ContentLength; documentMapping = new MappingLocation(
location: new SourceLocation(
_documentMapping.AbsoluteIndex,
_documentMapping.LineIndex,
_documentMapping.CharacterIndex),
contentLength: _generatedContentLength);
} }
_writer.LineMappingManager.AddMapping( _writer.LineMappingManager.AddMapping(
documentLocation: _documentMapping, documentLocation: documentMapping,
generatedLocation: new MappingLocation(_generatedLocation, _generatedContentLength)); generatedLocation: generatedLocation);
} }
if (_writePragmas) if (_writePragmas)
@ -105,7 +116,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
} }
_writer.WriteLineDefaultDirective() _writer.WriteLineDefaultDirective()
.WriteLineHiddenDirective(); .WriteLineHiddenDirective();
} }
// Reset indent back to when it was started // Reset indent back to when it was started

View File

@ -2,44 +2,71 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Globalization; using System.Globalization;
using Microsoft.Internal.Web.Utils;
namespace Microsoft.AspNet.Razor.Generator.Compiler namespace Microsoft.AspNet.Razor.Generator.Compiler
{ {
public class LineMapping public class LineMapping
{ {
public LineMapping()
: this(documentLocation: null, generatedLocation: null)
{
}
public LineMapping(MappingLocation documentLocation, MappingLocation generatedLocation) public LineMapping(MappingLocation documentLocation, MappingLocation generatedLocation)
{ {
DocumentLocation = documentLocation; DocumentLocation = documentLocation;
GeneratedLocation = generatedLocation; GeneratedLocation = generatedLocation;
} }
public MappingLocation DocumentLocation { get; set; } public MappingLocation DocumentLocation { get; }
public MappingLocation GeneratedLocation { get; set; }
public MappingLocation GeneratedLocation { get; }
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
var other = obj as LineMapping; var other = obj as LineMapping;
if (ReferenceEquals(other, null))
{
return false;
}
return DocumentLocation.Equals(other.DocumentLocation) && return DocumentLocation.Equals(other.DocumentLocation) &&
GeneratedLocation.Equals(other.GeneratedLocation); GeneratedLocation.Equals(other.GeneratedLocation);
} }
public override int GetHashCode() public override int GetHashCode()
{ {
return base.GetHashCode(); return HashCodeCombiner.Start()
.Add(DocumentLocation)
.Add(GeneratedLocation)
.CombinedHash;
} }
public static bool operator ==(LineMapping left, LineMapping right) 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); return left.Equals(right);
} }
public static bool operator !=(LineMapping left, LineMapping 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); return !left.Equals(right);
} }

View File

@ -12,15 +12,11 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler
Mappings = new List<LineMapping>(); Mappings = new List<LineMapping>();
} }
public List<LineMapping> Mappings { get; private set; } public List<LineMapping> Mappings { get; }
public void AddMapping(MappingLocation documentLocation, MappingLocation generatedLocation) public void AddMapping(MappingLocation documentLocation, MappingLocation generatedLocation)
{ {
Mappings.Add(new LineMapping Mappings.Add(new LineMapping(documentLocation, generatedLocation));
{
DocumentLocation = documentLocation,
GeneratedLocation = generatedLocation
});
} }
} }
} }

View File

@ -2,12 +2,15 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Globalization; using System.Globalization;
using Microsoft.Internal.Web.Utils;
namespace Microsoft.AspNet.Razor.Generator.Compiler namespace Microsoft.AspNet.Razor.Generator.Compiler
{ {
public class MappingLocation public class MappingLocation
{ {
public MappingLocation() : base() { } public MappingLocation()
{
}
public MappingLocation(SourceLocation location, int contentLength) public MappingLocation(SourceLocation location, int contentLength)
{ {
@ -17,24 +20,36 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler
CharacterIndex = location.CharacterIndex; CharacterIndex = location.CharacterIndex;
} }
public int ContentLength { get; set; } public int ContentLength { get; }
public int AbsoluteIndex { get; set; }
public int LineIndex { get; set; } public int AbsoluteIndex { get; }
public int CharacterIndex { get; set; }
public int LineIndex { get; }
public int CharacterIndex { get; }
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
var other = obj as MappingLocation; var other = obj as MappingLocation;
if (ReferenceEquals(other, null))
{
return false;
}
return AbsoluteIndex == other.AbsoluteIndex && return AbsoluteIndex == other.AbsoluteIndex &&
ContentLength == other.ContentLength && ContentLength == other.ContentLength &&
LineIndex == other.LineIndex && LineIndex == other.LineIndex &&
CharacterIndex == other.CharacterIndex; CharacterIndex == other.CharacterIndex;
} }
public override int GetHashCode() public override int GetHashCode()
{ {
return base.GetHashCode(); return HashCodeCombiner.Start()
.Add(AbsoluteIndex)
.Add(ContentLength)
.Add(LineIndex)
.Add(CharacterIndex)
.CombinedHash;
} }
public override string ToString() public override string ToString()
@ -49,11 +64,33 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler
public static bool operator ==(MappingLocation left, MappingLocation right) 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); return left.Equals(right);
} }
public static bool operator !=(MappingLocation left, MappingLocation 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); return !left.Equals(right);
} }
} }

View File

@ -1,12 +1,10 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Globalization; using System.Globalization;
using Microsoft.AspNet.Razor.Generator.Compiler; using Microsoft.AspNet.Razor.Generator.Compiler;
using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.Parser.SyntaxTree;
using Microsoft.AspNet.Razor.Text; using Microsoft.AspNet.Razor.Text;
using Microsoft.Internal.Web.Utils;
namespace Microsoft.AspNet.Razor.Generator namespace Microsoft.AspNet.Razor.Generator
{ {
@ -23,8 +21,9 @@ namespace Microsoft.AspNet.Razor.Generator
ValueStart = valueStart; ValueStart = valueStart;
} }
public LocationTagged<string> Prefix { get; private set; } public LocationTagged<string> Prefix { get; }
public SourceLocation ValueStart { get; private set; }
public SourceLocation ValueStart { get; }
public override void GenerateStartBlockCode(Block target, CodeGeneratorContext context) public override void GenerateStartBlockCode(Block target, CodeGeneratorContext context)
{ {
@ -47,14 +46,12 @@ namespace Microsoft.AspNet.Razor.Generator
{ {
var other = obj as DynamicAttributeBlockCodeGenerator; var other = obj as DynamicAttributeBlockCodeGenerator;
return other != null && return other != null &&
Equals(other.Prefix, Prefix); Equals(other.Prefix, Prefix);
} }
public override int GetHashCode() public override int GetHashCode()
{ {
return HashCodeCombiner.Start() return Prefix == null ? 0 : Prefix.GetHashCode();
.Add(Prefix)
.CombinedHash;
} }
} }
} }

View File

@ -8,6 +8,8 @@ namespace Microsoft.AspNet.Razor.Generator
{ {
public class ExpressionCodeGenerator : HybridCodeGenerator public class ExpressionCodeGenerator : HybridCodeGenerator
{ {
private static readonly int TypeHashCode = typeof(ExpressionCodeGenerator).GetHashCode();
public override void GenerateStartBlockCode(Block target, CodeGeneratorContext context) public override void GenerateStartBlockCode(Block target, CodeGeneratorContext context)
{ {
context.CodeTreeBuilder.StartChunkBlock<ExpressionBlockChunk>(target); context.CodeTreeBuilder.StartChunkBlock<ExpressionBlockChunk>(target);
@ -30,12 +32,13 @@ namespace Microsoft.AspNet.Razor.Generator
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return obj is ExpressionCodeGenerator; return obj != null &&
GetType() == obj.GetType();
} }
public override int GetHashCode() public override int GetHashCode()
{ {
return base.GetHashCode(); return TypeHashCode;
} }
} }
} }

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Microsoft.Framework.Internal; using Microsoft.Framework.Internal;
using Microsoft.Internal.Web.Utils;
namespace Microsoft.AspNet.Razor.Generator namespace Microsoft.AspNet.Razor.Generator
{ {
@ -121,12 +122,12 @@ namespace Microsoft.AspNet.Razor.Generator
} }
// Required Items // Required Items
public string WriteMethodName { get; private set; } public string WriteMethodName { get; }
public string WriteLiteralMethodName { get; private set; } public string WriteLiteralMethodName { get; }
public string WriteToMethodName { get; private set; } public string WriteToMethodName { get; }
public string WriteLiteralToMethodName { get; private set; } public string WriteLiteralToMethodName { get; }
public string ExecuteMethodName { get; private set; } public string ExecuteMethodName { get; }
public GeneratedTagHelperContext GeneratedTagHelperContext { get; private set; } public GeneratedTagHelperContext GeneratedTagHelperContext { get; }
// Optional Items // Optional Items
public string BeginContextMethodName { get; set; } public string BeginContextMethodName { get; set; }
@ -160,30 +161,29 @@ namespace Microsoft.AspNet.Razor.Generator
{ {
return false; return false;
} }
var other = (GeneratedClassContext)obj; var other = (GeneratedClassContext)obj;
return string.Equals(DefineSectionMethodName, other.DefineSectionMethodName, StringComparison.Ordinal) && return string.Equals(DefineSectionMethodName, other.DefineSectionMethodName, StringComparison.Ordinal) &&
string.Equals(WriteMethodName, other.WriteMethodName, StringComparison.Ordinal) && string.Equals(WriteMethodName, other.WriteMethodName, StringComparison.Ordinal) &&
string.Equals(WriteLiteralMethodName, other.WriteLiteralMethodName, StringComparison.Ordinal) && string.Equals(WriteLiteralMethodName, other.WriteLiteralMethodName, StringComparison.Ordinal) &&
string.Equals(WriteToMethodName, other.WriteToMethodName, StringComparison.Ordinal) && string.Equals(WriteToMethodName, other.WriteToMethodName, StringComparison.Ordinal) &&
string.Equals(WriteLiteralToMethodName, other.WriteLiteralToMethodName, StringComparison.Ordinal) && string.Equals(WriteLiteralToMethodName, other.WriteLiteralToMethodName, StringComparison.Ordinal) &&
string.Equals(ExecuteMethodName, other.ExecuteMethodName, StringComparison.Ordinal) && string.Equals(ExecuteMethodName, other.ExecuteMethodName, StringComparison.Ordinal) &&
string.Equals(TemplateTypeName, other.TemplateTypeName, StringComparison.Ordinal) && string.Equals(TemplateTypeName, other.TemplateTypeName, StringComparison.Ordinal) &&
string.Equals(BeginContextMethodName, other.BeginContextMethodName, StringComparison.Ordinal) && string.Equals(BeginContextMethodName, other.BeginContextMethodName, StringComparison.Ordinal) &&
string.Equals(EndContextMethodName, other.EndContextMethodName, StringComparison.Ordinal); string.Equals(EndContextMethodName, other.EndContextMethodName, StringComparison.Ordinal);
} }
public override int GetHashCode() public override int GetHashCode()
{ {
// TODO: Use HashCodeCombiner // Hash code should include only immutable properties.
return DefineSectionMethodName.GetHashCode() ^ return HashCodeCombiner.Start()
WriteMethodName.GetHashCode() ^ .Add(WriteMethodName, StringComparer.Ordinal)
WriteLiteralMethodName.GetHashCode() ^ .Add(WriteLiteralMethodName, StringComparer.Ordinal)
WriteToMethodName.GetHashCode() ^ .Add(WriteToMethodName, StringComparer.Ordinal)
WriteLiteralToMethodName.GetHashCode() ^ .Add(WriteLiteralToMethodName, StringComparer.Ordinal)
ExecuteMethodName.GetHashCode() ^ .Add(ExecuteMethodName, StringComparer.Ordinal)
TemplateTypeName.GetHashCode() ^ .CombinedHash;
BeginContextMethodName.GetHashCode() ^
EndContextMethodName.GetHashCode();
} }
public static bool operator ==(GeneratedClassContext left, GeneratedClassContext right) public static bool operator ==(GeneratedClassContext left, GeneratedClassContext right)

View File

@ -1,7 +1,6 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Globalization; using System.Globalization;
using Microsoft.AspNet.Razor.Generator.Compiler; using Microsoft.AspNet.Razor.Generator.Compiler;
using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.Parser.SyntaxTree;
@ -24,9 +23,11 @@ namespace Microsoft.AspNet.Razor.Generator
Value = value; Value = value;
} }
public LocationTagged<string> Prefix { get; private set; } public LocationTagged<string> Prefix { get; }
public LocationTagged<string> Value { get; private set; }
public LocationTagged<SpanCodeGenerator> ValueGenerator { get; private set; } public LocationTagged<string> Value { get; }
public LocationTagged<SpanCodeGenerator> ValueGenerator { get; }
public override void GenerateCode(Span target, CodeGeneratorContext context) public override void GenerateCode(Span target, CodeGeneratorContext context)
{ {
@ -62,9 +63,9 @@ namespace Microsoft.AspNet.Razor.Generator
{ {
var other = obj as LiteralAttributeCodeGenerator; var other = obj as LiteralAttributeCodeGenerator;
return other != null && return other != null &&
Equals(other.Prefix, Prefix) && Equals(other.Prefix, Prefix) &&
Equals(other.Value, Value) && Equals(other.Value, Value) &&
Equals(other.ValueGenerator, ValueGenerator); Equals(other.ValueGenerator, ValueGenerator);
} }
public override int GetHashCode() public override int GetHashCode()

View File

@ -16,15 +16,5 @@ namespace Microsoft.AspNet.Razor.Generator
{ {
return "Markup"; return "Markup";
} }
public override bool Equals(object obj)
{
return obj is MarkupCodeGenerator;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
} }
} }

View File

@ -1,7 +1,6 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.Parser.SyntaxTree;
namespace Microsoft.AspNet.Razor.Generator namespace Microsoft.AspNet.Razor.Generator
@ -25,15 +24,5 @@ namespace Microsoft.AspNet.Razor.Generator
{ {
return "VirtualPath"; return "VirtualPath";
} }
public override bool Equals(object obj)
{
return obj is ResolveUrlCodeGenerator;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
} }
} }

View File

@ -4,7 +4,6 @@
using System; using System;
using Microsoft.AspNet.Razor.Generator.Compiler; using Microsoft.AspNet.Razor.Generator.Compiler;
using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.Parser.SyntaxTree;
using Microsoft.Internal.Web.Utils;
namespace Microsoft.AspNet.Razor.Generator namespace Microsoft.AspNet.Razor.Generator
{ {
@ -15,7 +14,7 @@ namespace Microsoft.AspNet.Razor.Generator
SectionName = sectionName; SectionName = sectionName;
} }
public string SectionName { get; private set; } public string SectionName { get; }
public override void GenerateStartBlockCode(Block target, CodeGeneratorContext context) public override void GenerateStartBlockCode(Block target, CodeGeneratorContext context)
{ {
@ -32,17 +31,13 @@ namespace Microsoft.AspNet.Razor.Generator
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
var other = obj as SectionCodeGenerator; var other = obj as SectionCodeGenerator;
return other != null && return base.Equals(other) &&
base.Equals(other) && string.Equals(SectionName, other.SectionName, StringComparison.Ordinal);
string.Equals(SectionName, other.SectionName, StringComparison.Ordinal);
} }
public override int GetHashCode() public override int GetHashCode()
{ {
return HashCodeCombiner.Start() return SectionName == null ? 0 : StringComparer.Ordinal.GetHashCode(SectionName);
.Add(base.GetHashCode())
.Add(SectionName)
.CombinedHash;
} }
public override string ToString() public override string ToString()

View File

@ -13,7 +13,7 @@ namespace Microsoft.AspNet.Razor.Generator
BaseType = baseType; BaseType = baseType;
} }
public string BaseType { get; private set; } public string BaseType { get; }
public override void GenerateCode(Span target, CodeGeneratorContext context) public override void GenerateCode(Span target, CodeGeneratorContext context)
{ {
@ -29,7 +29,7 @@ namespace Microsoft.AspNet.Razor.Generator
{ {
var other = obj as SetBaseTypeCodeGenerator; var other = obj as SetBaseTypeCodeGenerator;
return other != null && return other != null &&
string.Equals(BaseType, other.BaseType, StringComparison.Ordinal); string.Equals(BaseType, other.BaseType, StringComparison.Ordinal);
} }
public override int GetHashCode() public override int GetHashCode()

View File

@ -8,6 +8,8 @@ namespace Microsoft.AspNet.Razor.Generator
{ {
public abstract class SpanCodeGenerator : ISpanCodeGenerator public abstract class SpanCodeGenerator : ISpanCodeGenerator
{ {
private static readonly int TypeHashCode = typeof(SpanCodeGenerator).GetHashCode();
[SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", Justification = "This class has no instance state")] [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", Justification = "This class has no instance state")]
public static readonly ISpanCodeGenerator Null = new NullSpanCodeGenerator(); public static readonly ISpanCodeGenerator Null = new NullSpanCodeGenerator();
@ -17,12 +19,13 @@ namespace Microsoft.AspNet.Razor.Generator
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return (obj as ISpanCodeGenerator) != null; return obj != null &&
GetType() == obj.GetType();
} }
public override int GetHashCode() public override int GetHashCode()
{ {
return base.GetHashCode(); return TypeHashCode;
} }
private class NullSpanCodeGenerator : ISpanCodeGenerator private class NullSpanCodeGenerator : ISpanCodeGenerator

View File

@ -16,15 +16,5 @@ namespace Microsoft.AspNet.Razor.Generator
{ {
return "Stmt"; return "Stmt";
} }
public override bool Equals(object obj)
{
return obj is StatementCodeGenerator;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
} }
} }

View File

@ -6,7 +6,7 @@ using Microsoft.AspNet.Razor.Parser.SyntaxTree;
namespace Microsoft.AspNet.Razor.Generator namespace Microsoft.AspNet.Razor.Generator
{ {
/// <summary> /// <summary>
/// A <see cref="SpanCodeGenerator"/> responsible for generating /// A <see cref="SpanCodeGenerator"/> responsible for generating
/// <see cref="Compiler.TagHelperPrefixDirectiveChunk"/>s. /// <see cref="Compiler.TagHelperPrefixDirectiveChunk"/>s.
/// </summary> /// </summary>
public class TagHelperPrefixDirectiveCodeGenerator : SpanCodeGenerator public class TagHelperPrefixDirectiveCodeGenerator : SpanCodeGenerator

View File

@ -16,16 +16,5 @@ namespace Microsoft.AspNet.Razor.Generator
{ {
return "TypeMember"; return "TypeMember";
} }
public override bool Equals(object obj)
{
return obj is TypeMemberCodeGenerator;
}
// C# complains at us if we don't provide an implementation, even one like this
public override int GetHashCode()
{
return base.GetHashCode();
}
} }
} }

View File

@ -117,7 +117,7 @@ namespace Microsoft.AspNet.Razor.Parser
} }
// Set up edit handler // Set up edit handler
var editHandler = new AutoCompleteEditHandler(Language.TokenizeString) { AutoCompleteAtEndOfSpan = true }; var editHandler = new AutoCompleteEditHandler(Language.TokenizeString, autoCompleteAtEndOfSpan: true);
Span.EditHandler = editHandler; Span.EditHandler = editHandler;
Span.Accept(CurrentSymbol); Span.Accept(CurrentSymbol);

View File

@ -8,6 +8,7 @@ using System.Globalization;
using System.Linq; using System.Linq;
using Microsoft.AspNet.Razor.Generator; using Microsoft.AspNet.Razor.Generator;
using Microsoft.AspNet.Razor.Text; using Microsoft.AspNet.Razor.Text;
using Microsoft.Internal.Web.Utils;
namespace Microsoft.AspNet.Razor.Parser.SyntaxTree namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
{ {
@ -45,11 +46,11 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
} }
[SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "Type is the most appropriate name for this property and there is little chance of confusion with GetType")] [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "Type is the most appropriate name for this property and there is little chance of confusion with GetType")]
public BlockType Type { get; private set; } public BlockType Type { get; }
public IEnumerable<SyntaxTreeNode> Children { get; private set; } public IEnumerable<SyntaxTreeNode> Children { get; }
public IBlockCodeGenerator CodeGenerator { get; private set; } public IBlockCodeGenerator CodeGenerator { get; }
public override bool IsBlock public override bool IsBlock
{ {
@ -111,14 +112,18 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
{ {
var other = obj as Block; var other = obj as Block;
return other != null && return other != null &&
Type == other.Type && Type == other.Type &&
Equals(CodeGenerator, other.CodeGenerator) && Equals(CodeGenerator, other.CodeGenerator) &&
ChildrenEqual(Children, other.Children); ChildrenEqual(Children, other.Children);
} }
public override int GetHashCode() public override int GetHashCode()
{ {
return (int)Type; return HashCodeCombiner.Start()
.Add(Type)
.Add(CodeGenerator)
.Add(Children)
.CombinedHash;
} }
public IEnumerable<Span> Flatten() public IEnumerable<Span> Flatten()
@ -201,7 +206,19 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
{ {
return false; return false;
} }
return Enumerable.SequenceEqual(Children, other.Children, new EquivalenceComparer()); return Enumerable.SequenceEqual(Children, other.Children, new EquivalenceComparer());
} }
public override int GetEquivalenceHash()
{
var combiner = HashCodeCombiner.Start().Add(Type);
foreach (var child in Children)
{
combiner.Add(child.GetEquivalenceHash());
}
return combiner.CombinedHash;
}
} }
} }

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
public int GetHashCode(SyntaxTreeNode obj) public int GetHashCode(SyntaxTreeNode obj)
{ {
return obj.GetHashCode(); return obj.GetEquivalenceHash();
} }
} }
} }

View File

@ -10,12 +10,12 @@ using Microsoft.AspNet.Razor.Editor;
using Microsoft.AspNet.Razor.Generator; using Microsoft.AspNet.Razor.Generator;
using Microsoft.AspNet.Razor.Text; using Microsoft.AspNet.Razor.Text;
using Microsoft.AspNet.Razor.Tokenizer.Symbols; using Microsoft.AspNet.Razor.Tokenizer.Symbols;
using Microsoft.Internal.Web.Utils;
namespace Microsoft.AspNet.Razor.Parser.SyntaxTree namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
{ {
public class Span : SyntaxTreeNode public class Span : SyntaxTreeNode
{ {
private static readonly int TypeHashCode = typeof(Span).GetHashCode();
private SourceLocation _start; private SourceLocation _start;
public Span(SpanBuilder builder) public Span(SpanBuilder builder)
@ -126,29 +126,32 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
{ {
var other = node as Span; var other = node as Span;
return other != null && return other != null &&
Kind.Equals(other.Kind) && Kind.Equals(other.Kind) &&
Start.Equals(other.Start) && Start.Equals(other.Start) &&
EditHandler.Equals(other.EditHandler) && EditHandler.Equals(other.EditHandler) &&
string.Equals(other.Content, Content, StringComparison.Ordinal); string.Equals(other.Content, Content, StringComparison.Ordinal);
}
public override int GetEquivalenceHash()
{
// Hash code should include only immutable properties but EquivalentTo also checks the type.
return TypeHashCode;
} }
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
var other = obj as Span; var other = obj as Span;
return other != null && return other != null &&
Kind.Equals(other.Kind) && Kind.Equals(other.Kind) &&
EditHandler.Equals(other.EditHandler) && EditHandler.Equals(other.EditHandler) &&
CodeGenerator.Equals(other.CodeGenerator) && CodeGenerator.Equals(other.CodeGenerator) &&
Symbols.SequenceEqual(other.Symbols); Symbols.SequenceEqual(other.Symbols);
} }
public override int GetHashCode() public override int GetHashCode()
{ {
return HashCodeCombiner.Start() // Hash code should include only immutable properties but Equals also checks the type.
.Add((int)Kind) return TypeHashCode;
.Add(Start)
.Add(Content)
.CombinedHash;
} }
} }
} }

View File

@ -37,5 +37,15 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
/// symbols may be different. /// symbols may be different.
/// </returns> /// </returns>
public abstract bool EquivalentTo(SyntaxTreeNode node); public abstract bool EquivalentTo(SyntaxTreeNode node);
/// <summary>
/// Determines a hash code for the <see cref="SyntaxTreeNode"/> using only information relevant in
/// <see cref="EquivalentTo"/> comparisons.
/// </summary>
/// <returns>
/// A hash code for the <see cref="SyntaxTreeNode"/> using only information relevant in
/// <see cref="EquivalentTo"/> comparisons.
/// </returns>
public abstract int GetEquivalenceHash();
} }
} }

View File

@ -81,7 +81,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers
/// <summary> /// <summary>
/// The HTML tag name. /// The HTML tag name.
/// </summary> /// </summary>
public string TagName { get; private set; } public string TagName { get; }
public override int Length public override int Length
{ {
@ -115,20 +115,18 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers
/// </returns> /// </returns>
public bool Equals(TagHelperBlock other) public bool Equals(TagHelperBlock other)
{ {
return other != null && return base.Equals(other) &&
TagName == other.TagName && string.Equals(TagName, other.TagName, StringComparison.OrdinalIgnoreCase) &&
Attributes.SequenceEqual(other.Attributes) && Attributes.SequenceEqual(other.Attributes);
base.Equals(other);
} }
/// <inheritdoc /> /// <inheritdoc />
public override int GetHashCode() public override int GetHashCode()
{ {
return HashCodeCombiner return HashCodeCombiner.Start()
.Start()
.Add(TagName)
.Add(Attributes)
.Add(base.GetHashCode()) .Add(base.GetHashCode())
.Add(TagName, StringComparer.OrdinalIgnoreCase)
.Add(Attributes)
.CombinedHash; .CombinedHash;
} }
} }

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Globalization; using System.Globalization;
using Microsoft.Internal.Web.Utils;
namespace Microsoft.AspNet.Razor namespace Microsoft.AspNet.Razor
{ {
@ -35,8 +36,22 @@ namespace Microsoft.AspNet.Razor
{ {
} }
/// <summary>
/// Gets (or sets) the message describing the error.
/// </summary>
/// <remarks>Set property is only accessible for deserialization purposes.</remarks>
public string Message { get; set; } public string Message { get; set; }
/// <summary>
/// Gets (or sets) the start position of the erroneous text.
/// </summary>
/// <remarks>Set property is only accessible for deserialization purposes.</remarks>
public SourceLocation Location { get; set; } public SourceLocation Location { get; set; }
/// <summary>
/// Gets or sets the length of the erroneous text.
/// </summary>
/// <remarks>Set property is only accessible for deserialization purposes.</remarks>
public int Length { get; set; } public int Length { get; set; }
public override string ToString() public override string ToString()
@ -46,19 +61,23 @@ namespace Microsoft.AspNet.Razor
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
var err = obj as RazorError; var error = obj as RazorError;
return (err != null) && Equals(err); return Equals(error);
} }
public override int GetHashCode() public override int GetHashCode()
{ {
return base.GetHashCode(); return HashCodeCombiner.Start()
.Add(Message, StringComparer.Ordinal)
.Add(Location)
.CombinedHash;
} }
public bool Equals(RazorError other) public bool Equals(RazorError other)
{ {
return string.Equals(other.Message, Message, StringComparison.Ordinal) && return other != null &&
Location.Equals(other.Location); string.Equals(other.Message, Message, StringComparison.Ordinal) &&
Location.Equals(other.Location);
} }
} }
} }

View File

@ -90,13 +90,13 @@ namespace Microsoft.AspNet.Razor
/// <inheritdoc /> /// <inheritdoc />
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return (obj is SourceLocation) && Equals((SourceLocation)obj); return obj is SourceLocation &&
Equals((SourceLocation)obj);
} }
/// <inheritdoc /> /// <inheritdoc />
public override int GetHashCode() public override int GetHashCode()
{ {
// LineIndex and CharacterIndex can be calculated from AbsoluteIndex and the document content.
return HashCodeCombiner.Start() return HashCodeCombiner.Start()
.Add(FilePath, StringComparer.Ordinal) .Add(FilePath, StringComparer.Ordinal)
.Add(AbsoluteIndex) .Add(AbsoluteIndex)
@ -106,10 +106,8 @@ namespace Microsoft.AspNet.Razor
/// <inheritdoc /> /// <inheritdoc />
public bool Equals(SourceLocation other) public bool Equals(SourceLocation other)
{ {
return string.Equals(FilePath, other.FilePath, StringComparison.Ordinal) && // LineIndex and CharacterIndex can be calculated from AbsoluteIndex and the document content.
AbsoluteIndex == other.AbsoluteIndex && return CompareTo(other) == 0;
LineIndex == other.LineIndex &&
CharacterIndex == other.CharacterIndex;
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@ -68,7 +68,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
/// <paramref name="attributes"/>. /// <paramref name="attributes"/>.
/// </summary> /// </summary>
/// <param name="prefix"> /// <param name="prefix">
/// Text used as a required prefix when matching HTML start and end tags in the Razor source to available /// Text used as a required prefix when matching HTML start and end tags in the Razor source to available
/// tag helpers. /// tag helpers.
/// </param> /// </param>
/// <param name="tagName">The tag name that the tag helper targets. '*' indicates a catch-all /// <param name="tagName">The tag name that the tag helper targets. '*' indicates a catch-all
@ -99,7 +99,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
} }
/// <summary> /// <summary>
/// Text used as a required prefix when matching HTML start and end tags in the Razor source to available /// Text used as a required prefix when matching HTML start and end tags in the Razor source to available
/// tag helpers. /// tag helpers.
/// </summary> /// </summary>
public string Prefix { get; private set; } public string Prefix { get; private set; }
@ -128,11 +128,11 @@ namespace Microsoft.AspNet.Razor.TagHelpers
/// <summary> /// <summary>
/// The list of attributes the tag helper expects. /// The list of attributes the tag helper expects.
/// </summary> /// </summary>
public IList<TagHelperAttributeDescriptor> Attributes { get; private set; } public IReadOnlyList<TagHelperAttributeDescriptor> Attributes { get; private set; }
/// <summary> /// <summary>
/// The list of required attribute names the tag helper expects to target an element. /// The list of required attribute names the tag helper expects to target an element.
/// </summary> /// </summary>
public IList<string> RequiredAttributes { get; private set; } public IReadOnlyList<string> RequiredAttributes { get; private set; }
} }
} }

View File

@ -40,10 +40,10 @@ namespace Microsoft.AspNet.Razor.TagHelpers
string.Equals(descriptorX.AssemblyName, descriptorY.AssemblyName, StringComparison.Ordinal) && string.Equals(descriptorX.AssemblyName, descriptorY.AssemblyName, StringComparison.Ordinal) &&
Enumerable.SequenceEqual( Enumerable.SequenceEqual(
descriptorX.RequiredAttributes.OrderBy( descriptorX.RequiredAttributes.OrderBy(
attribute => attribute, attribute => attribute,
StringComparer.OrdinalIgnoreCase), StringComparer.OrdinalIgnoreCase),
descriptorY.RequiredAttributes.OrderBy( descriptorY.RequiredAttributes.OrderBy(
attribute => attribute, attribute => attribute,
StringComparer.OrdinalIgnoreCase), StringComparer.OrdinalIgnoreCase),
StringComparer.OrdinalIgnoreCase); StringComparer.OrdinalIgnoreCase);
} }
@ -62,12 +62,11 @@ namespace Microsoft.AspNet.Razor.TagHelpers
.Add(descriptor.AssemblyName, StringComparer.Ordinal); .Add(descriptor.AssemblyName, StringComparer.Ordinal);
var attributes = descriptor.RequiredAttributes.OrderBy( var attributes = descriptor.RequiredAttributes.OrderBy(
attribute => attribute, attribute => attribute,
StringComparer.OrdinalIgnoreCase); StringComparer.OrdinalIgnoreCase);
foreach (var attribute in attributes) foreach (var attribute in attributes)
{ {
hashCodeCombiner.Add(attributes); hashCodeCombiner.Add(attribute, StringComparer.OrdinalIgnoreCase);
} }
return hashCodeCombiner.CombinedHash; return hashCodeCombiner.CombinedHash;

View File

@ -36,16 +36,16 @@ namespace Microsoft.AspNet.Razor.TagHelpers
/// <summary> /// <summary>
/// A <see cref="string"/> used to find tag helper <see cref="System.Type"/>s. /// A <see cref="string"/> used to find tag helper <see cref="System.Type"/>s.
/// </summary> /// </summary>
public string DirectiveText { get; private set; } public string DirectiveText { get; }
/// <summary> /// <summary>
/// The <see cref="TagHelperDirectiveType"/> of this directive. /// The <see cref="TagHelperDirectiveType"/> of this directive.
/// </summary> /// </summary>
public TagHelperDirectiveType DirectiveType { get; private set; } public TagHelperDirectiveType DirectiveType { get; }
/// <summary> /// <summary>
/// The <see cref="SourceLocation"/> of the directive. /// The <see cref="SourceLocation"/> of the directive.
/// </summary> /// </summary>
public SourceLocation Location { get; private set; } public SourceLocation Location { get; }
} }
} }

View File

@ -33,15 +33,20 @@ namespace Microsoft.AspNet.Razor.Text
Value = value; Value = value;
} }
public SourceLocation Location { get; private set; } public SourceLocation Location { get; }
public T Value { get; private set; }
public T Value { get; }
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
LocationTagged<T> other = obj as LocationTagged<T>; LocationTagged<T> other = obj as LocationTagged<T>;
return other != null && if (ReferenceEquals(other, null))
Equals(other.Location, Location) && {
Equals(other.Value, Value); return false;
}
return Equals(other.Location, Location) &&
Equals(other.Value, Value);
} }
public override int GetHashCode() public override int GetHashCode()

View File

@ -6,6 +6,7 @@ using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.Text; using System.Text;
using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.Parser.SyntaxTree;
using Microsoft.Internal.Web.Utils;
namespace Microsoft.AspNet.Razor.Text namespace Microsoft.AspNet.Razor.Text
{ {
@ -58,12 +59,17 @@ namespace Microsoft.AspNet.Razor.Text
OldBuffer = oldBuffer; OldBuffer = oldBuffer;
} }
public int OldPosition { get; private set; } public int OldPosition { get; }
public int NewPosition { get; private set; }
public int OldLength { get; private set; } public int NewPosition { get; }
public int NewLength { get; private set; }
public ITextBuffer NewBuffer { get; private set; } public int OldLength { get; }
public ITextBuffer OldBuffer { get; private set; }
public int NewLength { get; }
public ITextBuffer NewBuffer { get; }
public ITextBuffer OldBuffer { get; }
/// <remark> /// <remark>
/// Note: This property is not thread safe, and will move position on the textbuffer while being read. /// Note: This property is not thread safe, and will move position on the textbuffer while being read.
@ -120,13 +126,26 @@ namespace Microsoft.AspNet.Razor.Text
{ {
return false; return false;
} }
var change = (TextChange)obj; var change = (TextChange)obj;
return (change.OldPosition == OldPosition) && return change.OldPosition == OldPosition &&
(change.NewPosition == NewPosition) && change.NewPosition == NewPosition &&
(change.OldLength == OldLength) && change.OldLength == OldLength &&
(change.NewLength == NewLength) && change.NewLength == NewLength &&
OldBuffer.Equals(change.OldBuffer) && OldBuffer.Equals(change.OldBuffer) &&
NewBuffer.Equals(change.NewBuffer); NewBuffer.Equals(change.NewBuffer);
}
public override int GetHashCode()
{
return HashCodeCombiner.Start()
.Add(OldPosition)
.Add(NewPosition)
.Add(OldLength)
.Add(NewLength)
.Add(OldBuffer)
.Add(NewBuffer)
.CombinedHash;
} }
public string ApplyChange(string content, int changeOffset) public string ApplyChange(string content, int changeOffset)
@ -147,11 +166,6 @@ namespace Microsoft.AspNet.Razor.Text
return ApplyChange(span.Content, span.Start.AbsoluteIndex); return ApplyChange(span.Content, span.Start.AbsoluteIndex);
} }
public override int GetHashCode()
{
return OldPosition ^ NewPosition ^ OldLength ^ NewLength ^ NewBuffer.GetHashCode() ^ OldBuffer.GetHashCode();
}
public override string ToString() public override string ToString()
{ {
return string.Format(CultureInfo.CurrentCulture, "({0}:{1}) \"{3}\" -> ({0}:{2}) \"{4}\"", OldPosition, OldLength, NewLength, OldText, NewText); return string.Format(CultureInfo.CurrentCulture, "({0}:{1}) \"{3}\" -> ({0}:{2}) \"{4}\"", OldPosition, OldLength, NewLength, OldText, NewText);

View File

@ -35,12 +35,13 @@ namespace Microsoft.AspNet.Razor.Tokenizer.Symbols
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
var other = obj as CSharpSymbol; var other = obj as CSharpSymbol;
return base.Equals(obj) && other.Keyword == Keyword; return base.Equals(other) && other.Keyword == Keyword;
} }
public override int GetHashCode() public override int GetHashCode()
{ {
return base.GetHashCode() ^ Keyword.GetHashCode(); // Hash code should include only immutable properties.
return base.GetHashCode();
} }
} }
} }

View File

@ -29,26 +29,28 @@ namespace Microsoft.AspNet.Razor.Tokenizer.Symbols
} }
public SourceLocation Start { get; private set; } public SourceLocation Start { get; private set; }
public string Content { get; private set; }
public IEnumerable<RazorError> Errors { get; private set; } public string Content { get; }
public IEnumerable<RazorError> Errors { get; }
[SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "This is the most appropriate name for this property and conflicts are unlikely")] [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "This is the most appropriate name for this property and conflicts are unlikely")]
public TType Type { get; private set; } public TType Type { get; }
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
SymbolBase<TType> other = obj as SymbolBase<TType>; SymbolBase<TType> other = obj as SymbolBase<TType>;
return other != null && return other != null &&
Start.Equals(other.Start) && Start.Equals(other.Start) &&
string.Equals(Content, other.Content, StringComparison.Ordinal) && string.Equals(Content, other.Content, StringComparison.Ordinal) &&
Type.Equals(other.Type); Type.Equals(other.Type);
} }
public override int GetHashCode() public override int GetHashCode()
{ {
// Hash code should include only immutable properties.
return HashCodeCombiner.Start() return HashCodeCombiner.Start()
.Add(Start) .Add(Content, StringComparer.Ordinal)
.Add(Content)
.Add(Type) .Add(Type)
.CombinedHash; .CombinedHash;
} }

View File

@ -40,7 +40,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{ {
var hashCodeCombiner = HashCodeCombiner var hashCodeCombiner = HashCodeCombiner
.Start() .Start()
.Add(base.GetHashCode()) .Add(base.GetHashCode(descriptor))
.Add(descriptor.TagName, StringComparer.Ordinal) .Add(descriptor.TagName, StringComparer.Ordinal)
.Add(descriptor.Prefix); .Add(descriptor.Prefix);

View File

@ -0,0 +1,220 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq;
using Microsoft.AspNet.Razor.Editor;
using Microsoft.AspNet.Razor.Tokenizer.Symbols;
using Xunit;
namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
{
public class AutoCompleteEditHandlerTest
{
public static TheoryData<AutoCompleteEditHandler, AutoCompleteEditHandler> MatchingTestDataSet
{
get
{
return new TheoryData<AutoCompleteEditHandler, AutoCompleteEditHandler>
{
{
new AutoCompleteEditHandler(tokenizer: null, autoCompleteAtEndOfSpan: false)
{
AcceptedCharacters = AcceptedCharacters.AllWhiteSpace,
AutoCompleteString = "one string",
EditorHints = EditorHints.VirtualPath,
},
new AutoCompleteEditHandler(tokenizer: null, autoCompleteAtEndOfSpan: false)
{
AcceptedCharacters = AcceptedCharacters.AllWhiteSpace,
AutoCompleteString = "one string",
EditorHints = EditorHints.VirtualPath,
}
},
{
// Tokenizer not involved in equality check or hash code calculation.
new AutoCompleteEditHandler(tokenizer: null, autoCompleteAtEndOfSpan: true)
{
AcceptedCharacters = AcceptedCharacters.Any,
AutoCompleteString = "two string",
EditorHints = EditorHints.None,
},
new AutoCompleteEditHandler(
tokenizer: _ => Enumerable.Empty<ISymbol>(),
autoCompleteAtEndOfSpan: true)
{
AcceptedCharacters = AcceptedCharacters.Any,
AutoCompleteString = "two string",
EditorHints = EditorHints.None,
}
},
};
}
}
public static TheoryData<AutoCompleteEditHandler, object> NonMatchingTestDataSet
{
get
{
return new TheoryData<AutoCompleteEditHandler, object>
{
{
new AutoCompleteEditHandler(
tokenizer: _ => Enumerable.Empty<ISymbol>(),
autoCompleteAtEndOfSpan: false)
{
AcceptedCharacters = AcceptedCharacters.AllWhiteSpace,
AutoCompleteString = "three string",
EditorHints = EditorHints.VirtualPath,
},
null
},
{
new AutoCompleteEditHandler(
tokenizer: _ => Enumerable.Empty<ISymbol>(),
autoCompleteAtEndOfSpan: false)
{
AcceptedCharacters = AcceptedCharacters.AllWhiteSpace,
AutoCompleteString = "three string",
EditorHints = EditorHints.VirtualPath,
},
new object()
},
{
new AutoCompleteEditHandler(
tokenizer: _ => Enumerable.Empty<ISymbol>(),
autoCompleteAtEndOfSpan: false)
{
AcceptedCharacters = AcceptedCharacters.AllWhiteSpace,
AutoCompleteString = "three string",
EditorHints = EditorHints.VirtualPath,
},
new SpanEditHandler(tokenizer: _ => Enumerable.Empty<ISymbol>())
},
{
// Different AcceptedCharacters.
new AutoCompleteEditHandler(
tokenizer: _ => Enumerable.Empty<ISymbol>(),
autoCompleteAtEndOfSpan: true)
{
AcceptedCharacters = AcceptedCharacters.AllWhiteSpace,
AutoCompleteString = "three string",
EditorHints = EditorHints.VirtualPath,
},
new AutoCompleteEditHandler(
tokenizer: _ => Enumerable.Empty<ISymbol>(),
autoCompleteAtEndOfSpan: true)
{
AcceptedCharacters = AcceptedCharacters.AnyExceptNewline,
AutoCompleteString = "three string",
EditorHints = EditorHints.VirtualPath,
}
},
{
// Different AutoCompleteAtEndOfSpan.
new AutoCompleteEditHandler(tokenizer: null, autoCompleteAtEndOfSpan: false)
{
AcceptedCharacters = AcceptedCharacters.AnyExceptNewline,
AutoCompleteString = "four string",
EditorHints = EditorHints.None,
},
new AutoCompleteEditHandler(tokenizer: null, autoCompleteAtEndOfSpan: true)
{
AcceptedCharacters = AcceptedCharacters.AnyExceptNewline,
AutoCompleteString = "four string",
EditorHints = EditorHints.None,
}
},
{
// Different AutoCompleteString.
new AutoCompleteEditHandler(
tokenizer: _ => Enumerable.Empty<ISymbol>(),
autoCompleteAtEndOfSpan: true)
{
AcceptedCharacters = AcceptedCharacters.NewLine,
AutoCompleteString = "some string",
EditorHints = EditorHints.None,
},
new AutoCompleteEditHandler(
tokenizer: _ => Enumerable.Empty<ISymbol>(),
autoCompleteAtEndOfSpan: true)
{
AcceptedCharacters = AcceptedCharacters.NewLine,
AutoCompleteString = "different string",
EditorHints = EditorHints.None,
}
},
{
// Different AutoCompleteString (case sensitive).
new AutoCompleteEditHandler(tokenizer: null, autoCompleteAtEndOfSpan: false)
{
AcceptedCharacters = AcceptedCharacters.None,
AutoCompleteString = "some string",
EditorHints = EditorHints.VirtualPath,
},
new AutoCompleteEditHandler(tokenizer: null, autoCompleteAtEndOfSpan: false)
{
AcceptedCharacters = AcceptedCharacters.None,
AutoCompleteString = "Some String",
EditorHints = EditorHints.VirtualPath,
}
},
{
// Different EditorHints.
new AutoCompleteEditHandler(
tokenizer: _ => Enumerable.Empty<ISymbol>(),
autoCompleteAtEndOfSpan: true)
{
AcceptedCharacters = AcceptedCharacters.NonWhiteSpace,
AutoCompleteString = "five string",
EditorHints = EditorHints.VirtualPath,
},
new AutoCompleteEditHandler(
tokenizer: _ => Enumerable.Empty<ISymbol>(),
autoCompleteAtEndOfSpan: true)
{
AcceptedCharacters = AcceptedCharacters.NonWhiteSpace,
AutoCompleteString = "five string",
EditorHints = EditorHints.None,
}
},
};
}
}
[Theory]
[MemberData(nameof(MatchingTestDataSet))]
public void Equals_True_WhenExpected(AutoCompleteEditHandler leftObject, AutoCompleteEditHandler rightObject)
{
// Arrange & Act
var result = leftObject.Equals(rightObject);
// Assert
Assert.True(result);
}
[Theory]
[MemberData(nameof(NonMatchingTestDataSet))]
public void Equals_False_WhenExpected(AutoCompleteEditHandler leftObject, object rightObject)
{
// Arrange & Act
var result = leftObject.Equals(rightObject);
// Assert
Assert.False(result);
}
[Theory]
[MemberData(nameof(MatchingTestDataSet))]
public void GetHashCode_ReturnsSameValue_WhenEqual(
AutoCompleteEditHandler leftObject,
AutoCompleteEditHandler rightObject)
{
// Arrange & Act
var leftResult = leftObject.GetHashCode();
var rightResult = rightObject.GetHashCode();
// Assert
Assert.Equal(leftResult, rightResult);
}
}
}

View File

@ -0,0 +1,295 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Linq;
using Microsoft.AspNet.Razor.Editor;
using Microsoft.AspNet.Razor.Tokenizer.Symbols;
using Xunit;
namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
{
public class ImplicitExpressionEditHandlerTest
{
public static TheoryData<ImplicitExpressionEditHandler, ImplicitExpressionEditHandler> MatchingTestDataSet
{
get
{
return new TheoryData<ImplicitExpressionEditHandler, ImplicitExpressionEditHandler>
{
{
new ImplicitExpressionEditHandler(
tokenizer: null,
keywords: new HashSet<string>(),
acceptTrailingDot: false)
{
AcceptedCharacters = AcceptedCharacters.AllWhiteSpace,
EditorHints = EditorHints.VirtualPath,
},
new ImplicitExpressionEditHandler(
tokenizer: null,
keywords: new HashSet<string>(),
acceptTrailingDot: false)
{
AcceptedCharacters = AcceptedCharacters.AllWhiteSpace,
EditorHints = EditorHints.VirtualPath,
}
},
{
// Tokenizer not involved in equality check or hash code calculation.
new ImplicitExpressionEditHandler(
tokenizer: null,
keywords: new HashSet<string> { "keyword 1" },
acceptTrailingDot: true)
{
AcceptedCharacters = AcceptedCharacters.Any,
EditorHints = EditorHints.None,
},
new ImplicitExpressionEditHandler(
tokenizer: _ => Enumerable.Empty<ISymbol>(),
keywords: new HashSet<string> { "keyword 1" },
acceptTrailingDot: true)
{
AcceptedCharacters = AcceptedCharacters.Any,
EditorHints = EditorHints.None,
}
},
{
// Only comparers are different (HashSet's comparer does not affect GetHashCode of entries).
new ImplicitExpressionEditHandler(
tokenizer: _ => Enumerable.Empty<ISymbol>(),
keywords: new HashSet<string> { "keyword 2", "keyword 3" },
acceptTrailingDot: true)
{
AcceptedCharacters = AcceptedCharacters.Any,
EditorHints = EditorHints.None,
},
new ImplicitExpressionEditHandler(
tokenizer: _ => Enumerable.Empty<ISymbol>(),
keywords: new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "keyword 2", "keyword 3" },
acceptTrailingDot: true)
{
AcceptedCharacters = AcceptedCharacters.Any,
EditorHints = EditorHints.None,
}
},
};
}
}
public static TheoryData<ImplicitExpressionEditHandler, object> NonMatchingTestDataSet
{
get
{
return new TheoryData<ImplicitExpressionEditHandler, object>
{
{
new ImplicitExpressionEditHandler(
tokenizer: _ => null,
keywords: new HashSet<string> { "keyword 4" },
acceptTrailingDot: false)
{
AcceptedCharacters = AcceptedCharacters.WhiteSpace,
EditorHints = EditorHints.VirtualPath,
},
null
},
{
new ImplicitExpressionEditHandler(
tokenizer: _ => null,
keywords: new HashSet<string> { "keyword 4" },
acceptTrailingDot: false)
{
AcceptedCharacters = AcceptedCharacters.WhiteSpace,
EditorHints = EditorHints.VirtualPath,
},
new object()
},
{
new ImplicitExpressionEditHandler(
tokenizer: _ => null,
keywords: new HashSet<string> { "keyword 4" },
acceptTrailingDot: false)
{
AcceptedCharacters = AcceptedCharacters.WhiteSpace,
EditorHints = EditorHints.None,
},
new SpanEditHandler( tokenizer: _ => null)
},
{
// Different AcceptedCharacters.
new ImplicitExpressionEditHandler(
tokenizer: _ => Enumerable.Empty<ISymbol>(),
keywords: new HashSet<string> { "keyword 5" },
acceptTrailingDot: true)
{
AcceptedCharacters = AcceptedCharacters.AllWhiteSpace,
EditorHints = EditorHints.VirtualPath,
},
new ImplicitExpressionEditHandler(
tokenizer: _ => Enumerable.Empty<ISymbol>(),
keywords: new HashSet<string> { "keyword 5" },
acceptTrailingDot: true)
{
AcceptedCharacters = AcceptedCharacters.AnyExceptNewline,
EditorHints = EditorHints.VirtualPath,
}
},
{
// Different AcceptTrailingDot.
new ImplicitExpressionEditHandler(
tokenizer: _ => null,
keywords: new HashSet<string> { "keyword 6" },
acceptTrailingDot: false)
{
AcceptedCharacters = AcceptedCharacters.AnyExceptNewline,
EditorHints = EditorHints.VirtualPath,
},
new ImplicitExpressionEditHandler(
tokenizer: _ => null,
keywords: new HashSet<string> { "keyword 6" },
acceptTrailingDot: true)
{
AcceptedCharacters = AcceptedCharacters.AnyExceptNewline,
EditorHints = EditorHints.VirtualPath,
}
},
{
// Different Keywords.
new ImplicitExpressionEditHandler(
tokenizer: _ => Enumerable.Empty<ISymbol>(),
keywords: new HashSet<string> { "keyword 7" },
acceptTrailingDot: true)
{
AcceptedCharacters = AcceptedCharacters.NewLine,
EditorHints = EditorHints.None,
},
new ImplicitExpressionEditHandler(
tokenizer: _ => Enumerable.Empty<ISymbol>(),
keywords: new HashSet<string> { "keyword 8" },
acceptTrailingDot: true)
{
AcceptedCharacters = AcceptedCharacters.NewLine,
EditorHints = EditorHints.None,
}
},
{
// Different Keywords comparers (Equals uses left comparer).
new ImplicitExpressionEditHandler(
tokenizer: _ => null,
keywords: new HashSet<string> { "keyword 9" },
acceptTrailingDot: false)
{
AcceptedCharacters = AcceptedCharacters.None,
EditorHints = EditorHints.VirtualPath,
},
new ImplicitExpressionEditHandler(
tokenizer: _ => null,
keywords: new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "KEYWORD 9" },
acceptTrailingDot: false)
{
AcceptedCharacters = AcceptedCharacters.None,
EditorHints = EditorHints.VirtualPath,
}
},
{
// Different Keywords (count).
new ImplicitExpressionEditHandler(
tokenizer: _ => Enumerable.Empty<ISymbol>(),
keywords: new HashSet<string> { "keyword 10" },
acceptTrailingDot: true)
{
AcceptedCharacters = AcceptedCharacters.NonWhiteSpace,
EditorHints = EditorHints.None,
},
new ImplicitExpressionEditHandler(
tokenizer: _ => Enumerable.Empty<ISymbol>(),
keywords: new HashSet<string> { "keyword 10", "keyword 11" },
acceptTrailingDot: true)
{
AcceptedCharacters = AcceptedCharacters.NonWhiteSpace,
EditorHints = EditorHints.None,
}
},
{
// Different Keywords (count).
new ImplicitExpressionEditHandler(
tokenizer: null,
keywords: new HashSet<string> { "keyword 12" },
acceptTrailingDot: false)
{
AcceptedCharacters = AcceptedCharacters.WhiteSpace,
EditorHints = EditorHints.None,
},
new ImplicitExpressionEditHandler(
tokenizer: null,
keywords: new HashSet<string>(),
acceptTrailingDot: false)
{
AcceptedCharacters = AcceptedCharacters.WhiteSpace,
EditorHints = EditorHints.None,
}
},
{
// Different EditorHints.
new ImplicitExpressionEditHandler(
tokenizer: _ => Enumerable.Empty<ISymbol>(),
keywords: new HashSet<string> { "keyword 13" },
acceptTrailingDot: true)
{
AcceptedCharacters = AcceptedCharacters.AllWhiteSpace,
EditorHints = EditorHints.VirtualPath,
},
new ImplicitExpressionEditHandler(
tokenizer: _ => Enumerable.Empty<ISymbol>(),
keywords: new HashSet<string> { "keyword 13" },
acceptTrailingDot: true)
{
AcceptedCharacters = AcceptedCharacters.AllWhiteSpace,
EditorHints = EditorHints.None,
}
},
};
}
}
[Theory]
[MemberData(nameof(MatchingTestDataSet))]
public void Equals_True_WhenExpected(
ImplicitExpressionEditHandler leftObject,
ImplicitExpressionEditHandler rightObject)
{
// Arrange & Act
var result = leftObject.Equals(rightObject);
// Assert
Assert.True(result);
}
[Theory]
[MemberData(nameof(NonMatchingTestDataSet))]
public void Equals_False_WhenExpected(ImplicitExpressionEditHandler leftObject, object rightObject)
{
// Arrange & Act
var result = leftObject.Equals(rightObject);
// Assert
Assert.False(result);
}
[Theory]
[MemberData(nameof(MatchingTestDataSet))]
public void GetHashCode_ReturnsSameValue_WhenEqual(
ImplicitExpressionEditHandler leftObject,
ImplicitExpressionEditHandler rightObject)
{
// Arrange & Act
var leftResult = leftObject.GetHashCode();
var rightResult = rightObject.GetHashCode();
// Assert
Assert.Equal(leftResult, rightResult);
}
}
}

View File

@ -0,0 +1,165 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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;
using System.Linq;
using Microsoft.AspNet.Razor.Editor;
using Microsoft.AspNet.Razor.Tokenizer.Symbols;
using Xunit;
namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
{
public class SpanEditHandlerTest
{
public static TheoryData<SpanEditHandler, SpanEditHandler> MatchingTestDataSet
{
get
{
return new TheoryData<SpanEditHandler, SpanEditHandler>
{
{
new SpanEditHandler(tokenizer: null)
{
AcceptedCharacters = AcceptedCharacters.AllWhiteSpace,
EditorHints = EditorHints.VirtualPath,
},
new SpanEditHandler(tokenizer: null)
{
AcceptedCharacters = AcceptedCharacters.AllWhiteSpace,
EditorHints = EditorHints.VirtualPath,
}
},
{
// Tokenizer not involved in equality check or hash code calculation.
new SpanEditHandler(tokenizer: null)
{
AcceptedCharacters = AcceptedCharacters.Any,
EditorHints = EditorHints.None,
},
new SpanEditHandler(tokenizer: _ => Enumerable.Empty<ISymbol>())
{
AcceptedCharacters = AcceptedCharacters.Any,
EditorHints = EditorHints.None,
}
},
};
}
}
public static TheoryData<SpanEditHandler, object> NonMatchingTestDataSet
{
get
{
return new TheoryData<SpanEditHandler, object>
{
{
new SpanEditHandler(tokenizer: _ => Enumerable.Empty<ISymbol>())
{
AcceptedCharacters = AcceptedCharacters.AllWhiteSpace,
EditorHints = EditorHints.VirtualPath,
},
null
},
{
new SpanEditHandler(tokenizer: _ => Enumerable.Empty<ISymbol>())
{
AcceptedCharacters = AcceptedCharacters.AllWhiteSpace,
EditorHints = EditorHints.VirtualPath,
},
new object()
},
{
new SpanEditHandler(tokenizer: _ => Enumerable.Empty<ISymbol>())
{
AcceptedCharacters = AcceptedCharacters.Any,
EditorHints = EditorHints.None,
},
new AutoCompleteEditHandler(
tokenizer: _ => Enumerable.Empty<ISymbol>(),
autoCompleteAtEndOfSpan: true)
{
AcceptedCharacters = AcceptedCharacters.Any,
AutoCompleteString = "two string",
EditorHints = EditorHints.None,
}
},
{
new SpanEditHandler(tokenizer: null)
{
AcceptedCharacters = AcceptedCharacters.AnyExceptNewline,
EditorHints = EditorHints.VirtualPath,
},
new ImplicitExpressionEditHandler(
tokenizer: null,
keywords: new HashSet<string>(),
acceptTrailingDot: false)
{
AcceptedCharacters = AcceptedCharacters.AnyExceptNewline,
EditorHints = EditorHints.VirtualPath,
}
},
{
// Different AcceptedCharacters.
new SpanEditHandler(tokenizer: _ => Enumerable.Empty<ISymbol>())
{
AcceptedCharacters = AcceptedCharacters.AllWhiteSpace,
EditorHints = EditorHints.VirtualPath,
},
new SpanEditHandler(tokenizer: _ => Enumerable.Empty<ISymbol>())
{
AcceptedCharacters = AcceptedCharacters.AnyExceptNewline,
EditorHints = EditorHints.VirtualPath,
}
},
{
// Different EditorHints.
new SpanEditHandler(tokenizer: _ => Enumerable.Empty<ISymbol>())
{
AcceptedCharacters = AcceptedCharacters.NonWhiteSpace,
EditorHints = EditorHints.VirtualPath,
},
new SpanEditHandler(tokenizer: _ => Enumerable.Empty<ISymbol>())
{
AcceptedCharacters = AcceptedCharacters.NonWhiteSpace,
EditorHints = EditorHints.None,
}
},
};
}
}
[Theory]
[MemberData(nameof(MatchingTestDataSet))]
public void Equals_True_WhenExpected(SpanEditHandler leftObject, SpanEditHandler rightObject)
{
// Arrange & Act
var result = leftObject.Equals(rightObject);
// Assert
Assert.True(result);
}
[Theory]
[MemberData(nameof(NonMatchingTestDataSet))]
public void Equals_False_WhenExpected(SpanEditHandler leftObject, object rightObject)
{
// Arrange & Act
var result = leftObject.Equals(rightObject);
// Assert
Assert.False(result);
}
[Theory]
[MemberData(nameof(MatchingTestDataSet))]
public void GetHashCode_ReturnsSameValue_WhenEqual(SpanEditHandler leftObject, SpanEditHandler rightObject)
{
// Arrange & Act
var leftResult = leftObject.GetHashCode();
var rightResult = rightObject.GetHashCode();
// Assert
Assert.Equal(leftResult, rightResult);
}
}
}

View File

@ -6,14 +6,13 @@ using System.Globalization;
using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.Parser.SyntaxTree;
using Microsoft.AspNet.Razor.Text; using Microsoft.AspNet.Razor.Text;
using Microsoft.AspNet.Razor.Tokenizer.Symbols; using Microsoft.AspNet.Razor.Tokenizer.Symbols;
using Microsoft.Internal.Web.Utils;
namespace Microsoft.AspNet.Razor.Test.Framework namespace Microsoft.AspNet.Razor.Test.Framework
{ {
internal class RawTextSymbol : ISymbol internal class RawTextSymbol : ISymbol
{ {
public SourceLocation Start { get; private set; } public SourceLocation Start { get; private set; }
public string Content { get; private set; } public string Content { get; }
public RawTextSymbol(SourceLocation start, string content) public RawTextSymbol(SourceLocation start, string content)
{ {
@ -39,10 +38,8 @@ namespace Microsoft.AspNet.Razor.Test.Framework
public override int GetHashCode() public override int GetHashCode()
{ {
return HashCodeCombiner.Start() // Hash code should include only immutable properties.
.Add(Start) return Content == null ? 0 : Content.GetHashCode();
.Add(Content)
.CombinedHash;
} }
public void OffsetStart(SourceLocation documentStart) public void OffsetStart(SourceLocation documentStart)

View File

@ -243,7 +243,12 @@ namespace Microsoft.AspNet.Razor.Test.Framework
public static SpanConstructor AutoCompleteWith(this SpanConstructor self, string autoCompleteString, bool atEndOfSpan) public static SpanConstructor AutoCompleteWith(this SpanConstructor self, string autoCompleteString, bool atEndOfSpan)
{ {
return self.With(new AutoCompleteEditHandler(SpanConstructor.TestTokenizer) { AutoCompleteString = autoCompleteString, AutoCompleteAtEndOfSpan = atEndOfSpan }); return self.With(new AutoCompleteEditHandler(
SpanConstructor.TestTokenizer,
autoCompleteAtEndOfSpan: atEndOfSpan)
{
AutoCompleteString = autoCompleteString
});
} }
public static SpanConstructor WithEditorHints(this SpanConstructor self, EditorHints hints) public static SpanConstructor WithEditorHints(this SpanConstructor self, EditorHints hints)

View File

@ -0,0 +1,109 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.Razor.Generator
{
public class AddImportCodeGeneratorTest
{
public static TheoryData<AddImportCodeGenerator, AddImportCodeGenerator> MatchingTestDataSet
{
get
{
return new TheoryData<AddImportCodeGenerator, AddImportCodeGenerator>
{
{
new AddImportCodeGenerator(ns: null, namespaceKeywordLength: 3),
new AddImportCodeGenerator(ns: null, namespaceKeywordLength: 3)
},
{
new AddImportCodeGenerator(ns: "Fred", namespaceKeywordLength: 23),
new AddImportCodeGenerator(ns: "Fred", namespaceKeywordLength: 23)
},
};
}
}
public static TheoryData<AddImportCodeGenerator, object> NonMatchingTestDataSet
{
get
{
return new TheoryData<AddImportCodeGenerator, object>
{
{
new AddImportCodeGenerator(ns: null, namespaceKeywordLength: 0),
null
},
{
new AddImportCodeGenerator(ns: "Fred", namespaceKeywordLength: 23),
null
},
{
new AddImportCodeGenerator(ns: "Fred", namespaceKeywordLength: 23),
new object()
},
{
new AddImportCodeGenerator(ns: "Fred", namespaceKeywordLength: 23),
SpanCodeGenerator.Null
},
{
new AddImportCodeGenerator(ns: "Fred", namespaceKeywordLength: 23),
new StatementCodeGenerator()
},
{
// Different Namespace.
new AddImportCodeGenerator(ns: "Fred", namespaceKeywordLength: 3),
new AddImportCodeGenerator(ns: "Ginger", namespaceKeywordLength: 3)
},
{
// Different Namespace (case sensitive).
new AddImportCodeGenerator(ns: "fred", namespaceKeywordLength: 9),
new AddImportCodeGenerator(ns: "FRED", namespaceKeywordLength: 9)
},
{
// Different NamespaceKeywordLength.
new AddImportCodeGenerator(ns: null, namespaceKeywordLength: 0),
new AddImportCodeGenerator(ns: null, namespaceKeywordLength: 23)
},
};
}
}
[Theory]
[MemberData(nameof(MatchingTestDataSet))]
public void Equals_True_WhenExpected(AddImportCodeGenerator leftObject, AddImportCodeGenerator rightObject)
{
// Arrange & Act
var result = leftObject.Equals(rightObject);
// Assert
Assert.True(result);
}
[Theory]
[MemberData(nameof(NonMatchingTestDataSet))]
public void Equals_False_WhenExpected(AddImportCodeGenerator leftObject, object rightObject)
{
// Arrange & Act
var result = leftObject.Equals(rightObject);
// Assert
Assert.False(result);
}
[Theory]
[MemberData(nameof(MatchingTestDataSet))]
public void GetHashCode_ReturnsSameValue_WhenEqual(
AddImportCodeGenerator leftObject,
AddImportCodeGenerator rightObject)
{
// Arrange & Act
var leftResult = leftObject.GetHashCode();
var rightResult = rightObject.GetHashCode();
// Assert
Assert.Equal(leftResult, rightResult);
}
}
}

View File

@ -0,0 +1,157 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Razor.Text;
using Xunit;
namespace Microsoft.AspNet.Razor.Generator
{
public class AttributeBlockCodeGeneratorTest
{
public static TheoryData<AttributeBlockCodeGenerator, AttributeBlockCodeGenerator> MatchingTestDataSet
{
get
{
return new TheoryData<AttributeBlockCodeGenerator, AttributeBlockCodeGenerator>
{
{
new AttributeBlockCodeGenerator(name: null, prefix: null, suffix: null),
new AttributeBlockCodeGenerator(name: null, prefix: null, suffix: null)
},
{
new AttributeBlockCodeGenerator(
name: "Fred",
prefix: new LocationTagged<string>(value: "Ginger", offset: 0, line: 0, col: 0),
suffix: new LocationTagged<string>(value: "George", offset: 0, line: 0, col: 0)),
new AttributeBlockCodeGenerator(
name: "Fred",
prefix: new LocationTagged<string>(value: "Ginger", offset: 0, line: 0, col: 0),
suffix: new LocationTagged<string>(value: "George", offset: 0, line: 0, col: 0))
},
{
new AttributeBlockCodeGenerator(
name: "Fred",
prefix: new LocationTagged<string>(value: "Ginger", offset: 10, line: 11, col: 12),
suffix: new LocationTagged<string>(value: "George", offset: 13, line: 14, col: 15)),
new AttributeBlockCodeGenerator(
name: "Fred",
prefix: new LocationTagged<string>(value: "Ginger", offset: 10, line: 11, col: 12),
suffix: new LocationTagged<string>(value: "George", offset: 13, line: 14, col: 15))
},
};
}
}
public static TheoryData<AttributeBlockCodeGenerator, object> NonMatchingTestDataSet
{
get
{
return new TheoryData<AttributeBlockCodeGenerator, object>
{
{
new AttributeBlockCodeGenerator(name: null, prefix: null, suffix: null),
null
},
{
new AttributeBlockCodeGenerator(
name: "Fred",
prefix: new LocationTagged<string>(value: "Ginger", offset: 0, line: 0, col: 0),
suffix: new LocationTagged<string>(value: "George", offset: 0, line: 0, col: 0)),
null
},
{
new AttributeBlockCodeGenerator(
name: "Fred",
prefix: new LocationTagged<string>(value: "Ginger", offset: 10, line: 11, col: 12),
suffix: new LocationTagged<string>(value: "George", offset: 13, line: 14, col: 15)),
null
},
{
new AttributeBlockCodeGenerator(
name: "Fred",
prefix: new LocationTagged<string>(value: "Ginger", offset: 10, line: 11, col: 12),
suffix: new LocationTagged<string>(value: "George", offset: 13, line: 14, col: 15)),
new object()
},
{
new AttributeBlockCodeGenerator(
name: "Fred",
prefix: new LocationTagged<string>(value: "Ginger", offset: 10, line: 11, col: 12),
suffix: new LocationTagged<string>(value: "George", offset: 13, line: 14, col: 15)),
new RazorCommentCodeGenerator()
},
{
// Different Name.
new AttributeBlockCodeGenerator(name: "Fred", prefix: null, suffix: null),
new AttributeBlockCodeGenerator(name: "Ginger", prefix: null, suffix: null)
},
{
// Different Name (case sensitive).
new AttributeBlockCodeGenerator(name: "fred", prefix: null, suffix: null),
new AttributeBlockCodeGenerator(name: "FRED", prefix: null, suffix: null)
},
{
// Different Prefix.
new AttributeBlockCodeGenerator(
name: null,
prefix: new LocationTagged<string>(value: "Ginger", offset: 10, line: 11, col: 12),
suffix: null),
new AttributeBlockCodeGenerator(
name: null,
prefix: new LocationTagged<string>(value: "George", offset: 10, line: 11, col: 12),
suffix: null)
},
{
// Different Suffix.
new AttributeBlockCodeGenerator(
name: null,
prefix: null,
suffix: new LocationTagged<string>(value: "Ginger", offset: 10, line: 11, col: 12)),
new AttributeBlockCodeGenerator(
name: null,
prefix: null,
suffix: new LocationTagged<string>(value: "George", offset: 10, line: 11, col: 12))
},
};
}
}
[Theory]
[MemberData(nameof(MatchingTestDataSet))]
public void Equals_True_WhenExpected(
AttributeBlockCodeGenerator leftObject,
AttributeBlockCodeGenerator rightObject)
{
// Arrange & Act
var result = leftObject.Equals(rightObject);
// Assert
Assert.True(result);
}
[Theory]
[MemberData(nameof(NonMatchingTestDataSet))]
public void Equals_False_WhenExpected(AttributeBlockCodeGenerator leftObject, object rightObject)
{
// Arrange & Act
var result = leftObject.Equals(rightObject);
// Assert
Assert.False(result);
}
[Theory]
[MemberData(nameof(MatchingTestDataSet))]
public void GetHashCode_ReturnsSameValue_WhenEqual(
AttributeBlockCodeGenerator leftObject,
AttributeBlockCodeGenerator rightObject)
{
// Arrange & Act
var leftResult = leftObject.GetHashCode();
var rightResult = rightObject.GetHashCode();
// Assert
Assert.Equal(leftResult, rightResult);
}
}
}

View File

@ -0,0 +1,166 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Razor.Text;
using Xunit;
namespace Microsoft.AspNet.Razor.Generator
{
public class DynamicAttributeBlockCodeGeneratorTest
{
public static TheoryData<DynamicAttributeBlockCodeGenerator, DynamicAttributeBlockCodeGenerator> MatchingTestDataSet
{
get
{
return new TheoryData<DynamicAttributeBlockCodeGenerator, DynamicAttributeBlockCodeGenerator>
{
{
new DynamicAttributeBlockCodeGenerator(prefix: null, offset: 0, line: 0, col: 0),
new DynamicAttributeBlockCodeGenerator(prefix: null, offset: 0, line: 0, col: 0)
},
{
new DynamicAttributeBlockCodeGenerator(
prefix: new LocationTagged<string>(value: "Fred", offset: 0, line: 0, col: 0),
offset: 10,
line: 11,
col: 12),
new DynamicAttributeBlockCodeGenerator(
prefix: new LocationTagged<string>(value: "Fred", offset: 0, line: 0, col: 0),
offset: 10,
line: 11,
col: 12)
},
// ValueStart not involved in equality check or hash code calculation.
{
new DynamicAttributeBlockCodeGenerator(
prefix: new LocationTagged<string>(value: "Ginger", offset: 10, line: 11, col: 12),
offset: 10,
line: 11,
col: 12),
new DynamicAttributeBlockCodeGenerator(
prefix: new LocationTagged<string>(value: "Ginger", offset: 10, line: 11, col: 12),
offset: 100,
line: 11,
col: 12)
},
{
new DynamicAttributeBlockCodeGenerator(
prefix: new LocationTagged<string>(value: "George", offset: 10, line: 11, col: 12),
offset: 10,
line: 11,
col: 12),
new DynamicAttributeBlockCodeGenerator(
prefix: new LocationTagged<string>(value: "George", offset: 10, line: 11, col: 12),
offset: 10,
line: 110,
col: 12)
},
{
new DynamicAttributeBlockCodeGenerator(
prefix: new LocationTagged<string>(value: "Dean", offset: 10, line: 11, col: 12),
offset: 10,
line: 11,
col: 12),
new DynamicAttributeBlockCodeGenerator(
prefix: new LocationTagged<string>(value: "Dean", offset: 10, line: 11, col: 12),
offset: 10,
line: 11,
col: 120)
},
};
}
}
public static TheoryData<DynamicAttributeBlockCodeGenerator, object> NonMatchingTestDataSet
{
get
{
return new TheoryData<DynamicAttributeBlockCodeGenerator, object>
{
{
new DynamicAttributeBlockCodeGenerator(prefix: null, offset: 0, line: 0, col: 0),
null
},
{
new DynamicAttributeBlockCodeGenerator(
prefix: new LocationTagged<string>(value: "Ginger", offset: 0, line: 0, col: 0),
offset: 10,
line: 11,
col: 12),
null
},
{
new DynamicAttributeBlockCodeGenerator(
prefix: new LocationTagged<string>(value: "Ginger", offset: 10, line: 11, col: 12),
offset: 10,
line: 11,
col: 12),
new object()
},
{
new DynamicAttributeBlockCodeGenerator(
prefix: new LocationTagged<string>(value: "George", offset: 10, line: 11, col: 12),
offset: 10,
line: 11,
col: 12),
new AttributeBlockCodeGenerator(
name: "Fred",
prefix: new LocationTagged<string>(value: "Ginger", offset: 10, line: 11, col: 12),
suffix: new LocationTagged<string>(value: "George", offset: 13, line: 14, col: 15))
},
{
// Different Prefix.
new DynamicAttributeBlockCodeGenerator(
prefix: new LocationTagged<string>(value: "Ginger", offset: 10, line: 11, col: 12),
offset: 10,
line: 11,
col: 12),
new DynamicAttributeBlockCodeGenerator(
prefix: new LocationTagged<string>(value: "George", offset: 10, line: 11, col: 12),
offset: 10,
line: 11,
col: 12)
},
};
}
}
[Theory]
[MemberData(nameof(MatchingTestDataSet))]
public void Equals_True_WhenExpected(
DynamicAttributeBlockCodeGenerator leftObject,
DynamicAttributeBlockCodeGenerator rightObject)
{
// Arrange & Act
var result = leftObject.Equals(rightObject);
// Assert
Assert.True(result);
}
[Theory]
[MemberData(nameof(NonMatchingTestDataSet))]
public void Equals_False_WhenExpected(DynamicAttributeBlockCodeGenerator leftObject, object rightObject)
{
// Arrange & Act
var result = leftObject.Equals(rightObject);
// Assert
Assert.False(result);
}
[Theory]
[MemberData(nameof(MatchingTestDataSet))]
public void GetHashCode_ReturnsSameValue_WhenEqual(
DynamicAttributeBlockCodeGenerator leftObject,
DynamicAttributeBlockCodeGenerator rightObject)
{
// Arrange & Act
var leftResult = leftObject.GetHashCode();
var rightResult = rightObject.GetHashCode();
// Assert
Assert.Equal(leftResult, rightResult);
}
}
}

View File

@ -0,0 +1,92 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq;
using Microsoft.AspNet.Razor.TagHelpers;
using Xunit;
namespace Microsoft.AspNet.Razor.Generator
{
// Really tests underlying BlockCodeGenerator
public class RazorCommentCodeGeneratorTest
{
public static TheoryData<RazorCommentCodeGenerator, IBlockCodeGenerator> MatchingTestDataSet
{
get
{
return new TheoryData<RazorCommentCodeGenerator, IBlockCodeGenerator>
{
{ new RazorCommentCodeGenerator(), new RazorCommentCodeGenerator() },
};
}
}
public static TheoryData<IBlockCodeGenerator, object> NonMatchingTestDataSet
{
get
{
return new TheoryData<IBlockCodeGenerator, object>
{
{ new RazorCommentCodeGenerator(), null },
{ new RazorCommentCodeGenerator(), new object() },
{ new RazorCommentCodeGenerator(), BlockCodeGenerator.Null },
{
new RazorCommentCodeGenerator(),
new AttributeBlockCodeGenerator(name: null, prefix: null, suffix: null)
},
{
new RazorCommentCodeGenerator(),
new DynamicAttributeBlockCodeGenerator(prefix: null, offset: 0, line: 0, col: 0)
},
{ new RazorCommentCodeGenerator(), new ExpressionCodeGenerator() },
{ new RazorCommentCodeGenerator(), new SectionCodeGenerator(sectionName: null) },
{
new RazorCommentCodeGenerator(),
new TagHelperCodeGenerator(Enumerable.Empty<TagHelperDescriptor>())
},
{ new RazorCommentCodeGenerator(), new TemplateBlockCodeGenerator() },
{
new RazorCommentCodeGenerator(),
new AddImportCodeGenerator(ns: "Fred", namespaceKeywordLength: 0)
},
};
}
}
[Theory]
[MemberData(nameof(MatchingTestDataSet))]
public void Equals_True_WhenExpected(RazorCommentCodeGenerator leftObject, IBlockCodeGenerator rightObject)
{
// Arrange & Act
var result = leftObject.Equals(rightObject);
// Assert
Assert.True(result);
}
[Theory]
[MemberData(nameof(NonMatchingTestDataSet))]
public void Equals_False_WhenExpected(IBlockCodeGenerator leftObject, object rightObject)
{
// Arrange & Act
var result = leftObject.Equals(rightObject);
// Assert
Assert.False(result);
}
[Theory]
[MemberData(nameof(MatchingTestDataSet))]
public void GetHashCode_ReturnsSameValue_WhenEqual(
RazorCommentCodeGenerator leftObject,
IBlockCodeGenerator rightObject)
{
// Arrange & Act
var leftResult = leftObject.GetHashCode();
var rightResult = rightObject.GetHashCode();
// Assert
Assert.Equal(leftResult, rightResult);
}
}
}

View File

@ -1,8 +1,8 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.Parser.SyntaxTree;
using Microsoft.Internal.Web.Utils;
namespace Microsoft.AspNet.Razor.Test.Generator namespace Microsoft.AspNet.Razor.Test.Generator
{ {
@ -28,9 +28,11 @@ namespace Microsoft.AspNet.Razor.Test.Generator
{ {
} }
public SpanKind Kind { get; private set; } public SpanKind Kind { get; }
public int Start { get; private set; }
public int End { get; private set; } public int Start { get; }
public int End { get; }
public override string ToString() public override string ToString()
{ {
@ -40,20 +42,19 @@ namespace Microsoft.AspNet.Razor.Test.Generator
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
var other = obj as TestSpan; var other = obj as TestSpan;
return other != null &&
if (other != null) Kind == other.Kind &&
{ Start == other.Start &&
return (Kind == other.Kind) && End == other.End;
(Start == other.Start) &&
(End == other.End);
}
return false;
} }
public override int GetHashCode() public override int GetHashCode()
{ {
return base.GetHashCode(); return HashCodeCombiner.Start()
.Add(Kind)
.Add(Start)
.Add(End)
.CombinedHash;
} }
} }
} }

View File

@ -20,7 +20,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
Factory Factory
.MetaCode(SyntaxConstants.CSharp.TagHelperPrefixKeyword + " ") .MetaCode(SyntaxConstants.CSharp.TagHelperPrefixKeyword + " ")
.Accepts(AcceptedCharacters.None), .Accepts(AcceptedCharacters.None),
Factory.Code("\"\"").AsTagHelperPrefixDirective(""))); Factory.Code("\"\"")
.AsTagHelperPrefixDirective(string.Empty)));
} }
[Fact] [Fact]
@ -32,7 +33,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
Factory Factory
.MetaCode(SyntaxConstants.CSharp.TagHelperPrefixKeyword + " ") .MetaCode(SyntaxConstants.CSharp.TagHelperPrefixKeyword + " ")
.Accepts(AcceptedCharacters.None), .Accepts(AcceptedCharacters.None),
Factory.Code("\"Foo\"").AsTagHelperPrefixDirective("Foo"))); Factory.Code("\"Foo\"")
.AsTagHelperPrefixDirective("Foo")));
} }
[Fact] [Fact]
@ -44,7 +46,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
Factory Factory
.MetaCode(SyntaxConstants.CSharp.TagHelperPrefixKeyword + " ") .MetaCode(SyntaxConstants.CSharp.TagHelperPrefixKeyword + " ")
.Accepts(AcceptedCharacters.None), .Accepts(AcceptedCharacters.None),
Factory.EmptyCSharp().AsTagHelperPrefixDirective(string.Empty)), Factory.EmptyCSharp()
.AsStatement()
.Accepts(AcceptedCharacters.AnyExceptNewline)),
new RazorError( new RazorError(
RazorResources.FormatParseError_DirectiveMustHaveValue( RazorResources.FormatParseError_DirectiveMustHaveValue(
SyntaxConstants.CSharp.TagHelperPrefixKeyword), SyntaxConstants.CSharp.TagHelperPrefixKeyword),
@ -60,12 +64,13 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
Factory Factory
.MetaCode(SyntaxConstants.CSharp.TagHelperPrefixKeyword + " ") .MetaCode(SyntaxConstants.CSharp.TagHelperPrefixKeyword + " ")
.Accepts(AcceptedCharacters.None), .Accepts(AcceptedCharacters.None),
Factory.Code("\"Foo").AsTagHelperPrefixDirective("Foo")), Factory.Code("\"Foo")
new RazorError( .AsTagHelperPrefixDirective("Foo")),
RazorResources.ParseError_Unterminated_String_Literal, new RazorError(
absoluteIndex: 17, lineIndex: 0, columnIndex: 17), RazorResources.ParseError_Unterminated_String_Literal,
new RazorError( absoluteIndex: 17, lineIndex: 0, columnIndex: 17),
RazorResources.FormatParseError_DirectiveMustBeSurroundedByQuotes( new RazorError(
RazorResources.FormatParseError_DirectiveMustBeSurroundedByQuotes(
SyntaxConstants.CSharp.TagHelperPrefixKeyword), SyntaxConstants.CSharp.TagHelperPrefixKeyword),
absoluteIndex: 17, lineIndex: 0, columnIndex: 17)); absoluteIndex: 17, lineIndex: 0, columnIndex: 17));
} }
@ -79,7 +84,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
Factory Factory
.MetaCode(SyntaxConstants.CSharp.TagHelperPrefixKeyword + " ") .MetaCode(SyntaxConstants.CSharp.TagHelperPrefixKeyword + " ")
.Accepts(AcceptedCharacters.None), .Accepts(AcceptedCharacters.None),
Factory.Code("Foo\"").AsTagHelperPrefixDirective("Foo")), Factory.Code("Foo\"")
.AsStatement()
.Accepts(AcceptedCharacters.AnyExceptNewline)),
new RazorError( new RazorError(
RazorResources.ParseError_Unterminated_String_Literal, RazorResources.ParseError_Unterminated_String_Literal,
absoluteIndex: 20, lineIndex: 0, columnIndex: 20), absoluteIndex: 20, lineIndex: 0, columnIndex: 20),
@ -98,7 +105,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
Factory Factory
.MetaCode(SyntaxConstants.CSharp.TagHelperPrefixKeyword + " ") .MetaCode(SyntaxConstants.CSharp.TagHelperPrefixKeyword + " ")
.Accepts(AcceptedCharacters.None), .Accepts(AcceptedCharacters.None),
Factory.Code("Foo").AsTagHelperPrefixDirective("Foo")), Factory.Code("Foo")
.AsStatement()
.Accepts(AcceptedCharacters.AnyExceptNewline)),
new RazorError( new RazorError(
RazorResources.FormatParseError_DirectiveMustBeSurroundedByQuotes( RazorResources.FormatParseError_DirectiveMustBeSurroundedByQuotes(
SyntaxConstants.CSharp.TagHelperPrefixKeyword), SyntaxConstants.CSharp.TagHelperPrefixKeyword),
@ -113,7 +122,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
Factory.CodeTransition(), Factory.CodeTransition(),
Factory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ") Factory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ")
.Accepts(AcceptedCharacters.None), .Accepts(AcceptedCharacters.None),
Factory.Code("\"Foo\"").AsRemoveTagHelper("Foo"))); Factory.Code("\"Foo\"")
.AsRemoveTagHelper("Foo")));
} }
[Fact] [Fact]
@ -124,7 +134,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
Factory.CodeTransition(), Factory.CodeTransition(),
Factory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ") Factory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ")
.Accepts(AcceptedCharacters.None), .Accepts(AcceptedCharacters.None),
Factory.Code("\" Foo, Bar \" ").AsRemoveTagHelper(" Foo, Bar "))); Factory.Code("\" Foo, Bar \" ")
.AsRemoveTagHelper("Foo, Bar")
.Accepts(AcceptedCharacters.AnyExceptNewline)));
} }
[Fact] [Fact]
@ -135,7 +147,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
Factory.CodeTransition(), Factory.CodeTransition(),
Factory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ") Factory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ")
.Accepts(AcceptedCharacters.None), .Accepts(AcceptedCharacters.None),
Factory.EmptyCSharp().AsRemoveTagHelper(string.Empty)), Factory.EmptyCSharp()
.AsStatement()
.Accepts(AcceptedCharacters.AnyExceptNewline)),
new RazorError( new RazorError(
RazorResources.FormatParseError_DirectiveMustHaveValue( RazorResources.FormatParseError_DirectiveMustHaveValue(
SyntaxConstants.CSharp.RemoveTagHelperKeyword), SyntaxConstants.CSharp.RemoveTagHelperKeyword),
@ -150,7 +164,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
Factory.CodeTransition(), Factory.CodeTransition(),
Factory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ") Factory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ")
.Accepts(AcceptedCharacters.None), .Accepts(AcceptedCharacters.None),
Factory.Code("\"Foo").AsRemoveTagHelper("Foo")), Factory.Code("\"Foo")
.AsRemoveTagHelper("Foo")),
new RazorError( new RazorError(
RazorResources.ParseError_Unterminated_String_Literal, RazorResources.ParseError_Unterminated_String_Literal,
absoluteIndex: 17, lineIndex: 0, columnIndex: 17), absoluteIndex: 17, lineIndex: 0, columnIndex: 17),
@ -168,7 +183,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
Factory.CodeTransition(), Factory.CodeTransition(),
Factory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ") Factory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ")
.Accepts(AcceptedCharacters.None), .Accepts(AcceptedCharacters.None),
Factory.Code("Foo\"").AsRemoveTagHelper("Foo")), Factory.Code("Foo\"")
.AsStatement()
.Accepts(AcceptedCharacters.AnyExceptNewline)),
new RazorError( new RazorError(
RazorResources.ParseError_Unterminated_String_Literal, RazorResources.ParseError_Unterminated_String_Literal,
absoluteIndex: 20, lineIndex: 0, columnIndex: 20), absoluteIndex: 20, lineIndex: 0, columnIndex: 20),
@ -186,7 +203,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
Factory.CodeTransition(), Factory.CodeTransition(),
Factory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ") Factory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ")
.Accepts(AcceptedCharacters.None), .Accepts(AcceptedCharacters.None),
Factory.Code("Foo").AsRemoveTagHelper("Foo")), Factory.Code("Foo")
.AsStatement()
.Accepts(AcceptedCharacters.AnyExceptNewline)),
new RazorError( new RazorError(
RazorResources.FormatParseError_DirectiveMustBeSurroundedByQuotes( RazorResources.FormatParseError_DirectiveMustBeSurroundedByQuotes(
SyntaxConstants.CSharp.RemoveTagHelperKeyword), SyntaxConstants.CSharp.RemoveTagHelperKeyword),
@ -201,7 +220,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
Factory.CodeTransition(), Factory.CodeTransition(),
Factory.MetaCode(SyntaxConstants.CSharp.AddTagHelperKeyword + " ") Factory.MetaCode(SyntaxConstants.CSharp.AddTagHelperKeyword + " ")
.Accepts(AcceptedCharacters.None), .Accepts(AcceptedCharacters.None),
Factory.Code("\"Foo\"").AsAddTagHelper("Foo"))); Factory.Code("\"Foo\"")
.AsAddTagHelper("Foo")));
} }
[Fact] [Fact]
@ -212,7 +232,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
Factory.CodeTransition(), Factory.CodeTransition(),
Factory.MetaCode(SyntaxConstants.CSharp.AddTagHelperKeyword + " ") Factory.MetaCode(SyntaxConstants.CSharp.AddTagHelperKeyword + " ")
.Accepts(AcceptedCharacters.None), .Accepts(AcceptedCharacters.None),
Factory.Code("\" Foo, Bar \" ").AsAddTagHelper(" Foo, Bar "))); Factory.Code("\" Foo, Bar \" ")
.AsAddTagHelper("Foo, Bar")));
} }
[Fact] [Fact]
@ -223,7 +244,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
Factory.CodeTransition(), Factory.CodeTransition(),
Factory.MetaCode(SyntaxConstants.CSharp.AddTagHelperKeyword + " ") Factory.MetaCode(SyntaxConstants.CSharp.AddTagHelperKeyword + " ")
.Accepts(AcceptedCharacters.None), .Accepts(AcceptedCharacters.None),
Factory.EmptyCSharp().AsAddTagHelper(string.Empty)), Factory.EmptyCSharp()
.AsStatement()
.Accepts(AcceptedCharacters.AnyExceptNewline)),
new RazorError( new RazorError(
RazorResources.FormatParseError_DirectiveMustHaveValue(SyntaxConstants.CSharp.AddTagHelperKeyword), RazorResources.FormatParseError_DirectiveMustHaveValue(SyntaxConstants.CSharp.AddTagHelperKeyword),
absoluteIndex: 14, lineIndex: 0, columnIndex: 14)); absoluteIndex: 14, lineIndex: 0, columnIndex: 14));
@ -237,7 +260,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
Factory.CodeTransition(), Factory.CodeTransition(),
Factory.MetaCode(SyntaxConstants.CSharp.AddTagHelperKeyword + " ") Factory.MetaCode(SyntaxConstants.CSharp.AddTagHelperKeyword + " ")
.Accepts(AcceptedCharacters.None), .Accepts(AcceptedCharacters.None),
Factory.Code("\"Foo").AsAddTagHelper("Foo")), Factory.Code("\"Foo")
.AsAddTagHelper("Foo")),
new RazorError( new RazorError(
RazorResources.ParseError_Unterminated_String_Literal, RazorResources.ParseError_Unterminated_String_Literal,
absoluteIndex: 14, lineIndex: 0, columnIndex: 14), absoluteIndex: 14, lineIndex: 0, columnIndex: 14),
@ -255,7 +279,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
Factory.CodeTransition(), Factory.CodeTransition(),
Factory.MetaCode(SyntaxConstants.CSharp.AddTagHelperKeyword + " ") Factory.MetaCode(SyntaxConstants.CSharp.AddTagHelperKeyword + " ")
.Accepts(AcceptedCharacters.None), .Accepts(AcceptedCharacters.None),
Factory.Code("Foo\"").AsAddTagHelper("Foo")), Factory.Code("Foo\"")
.AsStatement()
.Accepts(AcceptedCharacters.AnyExceptNewline)),
new RazorError( new RazorError(
RazorResources.ParseError_Unterminated_String_Literal, RazorResources.ParseError_Unterminated_String_Literal,
absoluteIndex: 17, lineIndex: 0, columnIndex: 17), absoluteIndex: 17, lineIndex: 0, columnIndex: 17),
@ -273,7 +299,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
Factory.CodeTransition(), Factory.CodeTransition(),
Factory.MetaCode(SyntaxConstants.CSharp.AddTagHelperKeyword + " ") Factory.MetaCode(SyntaxConstants.CSharp.AddTagHelperKeyword + " ")
.Accepts(AcceptedCharacters.None), .Accepts(AcceptedCharacters.None),
Factory.Code("Foo").AsAddTagHelper("Foo")), Factory.Code("Foo")
.AsStatement()
.Accepts(AcceptedCharacters.AnyExceptNewline)),
new RazorError( new RazorError(
RazorResources.FormatParseError_DirectiveMustBeSurroundedByQuotes( RazorResources.FormatParseError_DirectiveMustBeSurroundedByQuotes(
SyntaxConstants.CSharp.AddTagHelperKeyword), SyntaxConstants.CSharp.AddTagHelperKeyword),
@ -349,7 +377,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
Factory.MetaCode(SyntaxConstants.CSharp.FunctionsKeyword + " {") Factory.MetaCode(SyntaxConstants.CSharp.FunctionsKeyword + " {")
.Accepts(AcceptedCharacters.None), .Accepts(AcceptedCharacters.None),
Factory.Code(" foo(); bar(); ") Factory.Code(" foo(); bar(); ")
.AsFunctionsBody(), .AsFunctionsBody()
.AutoCompleteWith(autoCompleteString: null),
Factory.MetaCode("}") Factory.MetaCode("}")
.Accepts(AcceptedCharacters.None))); .Accepts(AcceptedCharacters.None)));
} }
@ -363,7 +392,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
Factory.MetaCode(SyntaxConstants.CSharp.FunctionsKeyword + " {") Factory.MetaCode(SyntaxConstants.CSharp.FunctionsKeyword + " {")
.Accepts(AcceptedCharacters.None), .Accepts(AcceptedCharacters.None),
Factory.Code(" ") Factory.Code(" ")
.AsFunctionsBody(), .AsFunctionsBody()
.AutoCompleteWith(autoCompleteString: null),
Factory.MetaCode("}") Factory.MetaCode("}")
.Accepts(AcceptedCharacters.None))); .Accepts(AcceptedCharacters.None)));
} }

View File

@ -57,7 +57,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
ParseBlockTest("{}", ParseBlockTest("{}",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.EmptyCSharp().AsStatement(), Factory.EmptyCSharp()
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
Factory.MetaCode("}").Accepts(AcceptedCharacters.None) Factory.MetaCode("}").Accepts(AcceptedCharacters.None)
)); ));
} }
@ -82,7 +84,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
+ "}", + "}",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code(Environment.NewLine + " ").AsStatement(), Factory.Code(Environment.NewLine + " ")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
new ExpressionBlock( new ExpressionBlock(
Factory.CodeTransition(), Factory.CodeTransition(),
Factory.EmptyCSharp() Factory.EmptyCSharp()
@ -101,7 +105,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
+ " @", + " @",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code(Environment.NewLine + " ").AsStatement(), Factory.Code(Environment.NewLine + " ")
.AsStatement()
.AutoCompleteWith("}"),
new ExpressionBlock( new ExpressionBlock(
Factory.CodeTransition(), Factory.CodeTransition(),
Factory.EmptyCSharp() Factory.EmptyCSharp()
@ -247,8 +253,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
ParseBlockTest("{ var foo = bar; if(foo != null) { bar(); } ", ParseBlockTest("{ var foo = bar; if(foo != null) { bar(); } ",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code(" var foo = bar; if(foo != null) { bar(); } ").AsStatement() Factory.Code(" var foo = bar; if(foo != null) { bar(); } ")
), .AsStatement()
.AutoCompleteWith("}")),
new RazorError( new RazorError(
RazorResources.FormatParseError_Expected_EndOfBlock_Before_EOF(RazorResources.BlockName_Code, '}', '{'), RazorResources.FormatParseError_Expected_EndOfBlock_Before_EOF(RazorResources.BlockName_Code, '}', '{'),
SourceLocation.Zero)); SourceLocation.Zero));
@ -260,8 +267,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
ParseBlockTest("functions { var foo = bar; if(foo != null) { bar(); } ", ParseBlockTest("functions { var foo = bar; if(foo != null) { bar(); } ",
new FunctionsBlock( new FunctionsBlock(
Factory.MetaCode("functions {").Accepts(AcceptedCharacters.None), Factory.MetaCode("functions {").Accepts(AcceptedCharacters.None),
Factory.Code(" var foo = bar; if(foo != null) { bar(); } ").AsFunctionsBody() Factory.Code(" var foo = bar; if(foo != null) { bar(); } ")
), .AsFunctionsBody()
.AutoCompleteWith("}")),
new RazorError( new RazorError(
RazorResources.FormatParseError_Expected_EndOfBlock_Before_EOF("functions", '}', '{'), RazorResources.FormatParseError_Expected_EndOfBlock_Before_EOF("functions", '}', '{'),
SourceLocation.Zero)); SourceLocation.Zero));
@ -592,7 +600,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code("string.Format(") Factory.Code("string.Format(")
.AsStatement(), .AsStatement()
.AutoCompleteWith(autoCompleteString: null),
new MarkupBlock( new MarkupBlock(
BlockFactory.MarkupTagBlock("<html>", AcceptedCharacters.None), BlockFactory.MarkupTagBlock("<html>", AcceptedCharacters.None),
BlockFactory.MarkupTagBlock("</html>", AcceptedCharacters.None)), BlockFactory.MarkupTagBlock("</html>", AcceptedCharacters.None)),

View File

@ -122,7 +122,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
new StatementBlock( new StatementBlock(
Factory.CodeTransition(), Factory.CodeTransition(),
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code(Environment.NewLine).AsStatement(), Factory.Code(Environment.NewLine)
.AsStatement()
.AutoCompleteWith("}"),
new MarkupBlock( new MarkupBlock(
Factory.Markup(" "), Factory.Markup(" "),
new MarkupTagBlock( new MarkupTagBlock(
@ -158,7 +160,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
Factory.CodeTransition(), Factory.CodeTransition(),
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.EmptyCSharp() Factory.EmptyCSharp()
.AsStatement(), .AsStatement()
.AutoCompleteWith("}"),
new CommentBlock( new CommentBlock(
Factory.CodeTransition(CSharpSymbolType.RazorCommentTransition) Factory.CodeTransition(CSharpSymbolType.RazorCommentTransition)
.Accepts(AcceptedCharacters.None), .Accepts(AcceptedCharacters.None),

View File

@ -53,7 +53,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
ParseBlockTest("{ using Foo.Bar.Baz; var foo = bar; }", ParseBlockTest("{ using Foo.Bar.Baz; var foo = bar; }",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code(" using Foo.Bar.Baz; var foo = bar; ").AsStatement(), Factory.Code(" using Foo.Bar.Baz; var foo = bar; ")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
Factory.MetaCode("}").Accepts(AcceptedCharacters.None) Factory.MetaCode("}").Accepts(AcceptedCharacters.None)
), ),
new RazorError( new RazorError(
@ -67,7 +69,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
ParseBlockTest("{ using Foo = Bar.Baz; var foo = bar; }", ParseBlockTest("{ using Foo = Bar.Baz; var foo = bar; }",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code(" using Foo = Bar.Baz; var foo = bar; ").AsStatement(), Factory.Code(" using Foo = Bar.Baz; var foo = bar; ")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
Factory.MetaCode("}").Accepts(AcceptedCharacters.None) Factory.MetaCode("}").Accepts(AcceptedCharacters.None)
), ),
new RazorError( new RazorError(
@ -81,7 +85,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
ParseBlockTest("{ functions Foo; }", ParseBlockTest("{ functions Foo; }",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code(" functions Foo; ").AsStatement(), Factory.Code(" functions Foo; ")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
Factory.MetaCode("}").Accepts(AcceptedCharacters.None) Factory.MetaCode("}").Accepts(AcceptedCharacters.None)
)); ));
} }
@ -94,7 +100,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
+ "}", + "}",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code($"{Environment.NewLine} List<dynamic> photos = gallery.Photo.ToList();{Environment.NewLine}").AsStatement(), Factory.Code($"{Environment.NewLine} List<dynamic> photos = gallery.Photo.ToList();{Environment.NewLine}")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
Factory.MetaCode("}").Accepts(AcceptedCharacters.None) Factory.MetaCode("}").Accepts(AcceptedCharacters.None)
)); ));
} }
@ -109,7 +117,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
ParseBlockTest("{" + code + "}", ParseBlockTest("{" + code + "}",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code(code).AsStatement(), Factory.Code(code)
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
Factory.MetaCode("}").Accepts(AcceptedCharacters.None) Factory.MetaCode("}").Accepts(AcceptedCharacters.None)
)); ));
} }
@ -136,7 +146,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
ParseBlockTest("functions {" + code + "} zoop", ParseBlockTest("functions {" + code + "} zoop",
new FunctionsBlock( new FunctionsBlock(
Factory.MetaCode("functions {").Accepts(AcceptedCharacters.None), Factory.MetaCode("functions {").Accepts(AcceptedCharacters.None),
Factory.Code(code).AsFunctionsBody(), Factory.Code(code)
.AsFunctionsBody()
.AutoCompleteWith(autoCompleteString: null),
Factory.MetaCode("}").Accepts(AcceptedCharacters.None) Factory.MetaCode("}").Accepts(AcceptedCharacters.None)
)); ));
} }
@ -147,7 +159,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
ParseBlockTest("functions { { { { { } zoop", ParseBlockTest("functions { { { { { } zoop",
new FunctionsBlock( new FunctionsBlock(
Factory.MetaCode("functions {").Accepts(AcceptedCharacters.None), Factory.MetaCode("functions {").Accepts(AcceptedCharacters.None),
Factory.Code(" { { { { } zoop").AsFunctionsBody() Factory.Code(" { { { { } zoop")
.AsFunctionsBody()
.AutoCompleteWith("}")
), ),
new RazorError( new RazorError(
RazorResources.FormatParseError_Expected_EndOfBlock_Before_EOF("functions", "}", "{"), RazorResources.FormatParseError_Expected_EndOfBlock_Before_EOF("functions", "}", "{"),

View File

@ -82,7 +82,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
+ "; }", + "; }",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code(" var foo = ").AsStatement(), Factory.Code(" var foo = ")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
new TemplateBlock( new TemplateBlock(
new MarkupBlock( new MarkupBlock(
Factory.MarkupTransition(), Factory.MarkupTransition(),
@ -104,7 +106,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
+ "}", + "}",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code("i").AsStatement(), Factory.Code("i")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
new TemplateBlock( new TemplateBlock(
new MarkupBlock( new MarkupBlock(
Factory.MarkupTransition(), Factory.MarkupTransition(),
@ -210,7 +214,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
{ {
ParseBlockTest("foreach(foo in Bar) { Html.ExecuteTemplate(foo," + TestNestedTemplateCode + "); }", ParseBlockTest("foreach(foo in Bar) { Html.ExecuteTemplate(foo," + TestNestedTemplateCode + "); }",
new StatementBlock( new StatementBlock(
Factory.Code("foreach(foo in Bar) { Html.ExecuteTemplate(foo, ").AsStatement(), Factory.Code("foreach(foo in Bar) { Html.ExecuteTemplate(foo, ")
.AsStatement(),
TestNestedTemplate(), TestNestedTemplate(),
Factory.Code("); }") Factory.Code("); }")
.AsStatement() .AsStatement()
@ -225,7 +230,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
ParseBlockTest("{ var foo = bar; Html.ExecuteTemplate(foo," + TestTemplateCode + "); }", ParseBlockTest("{ var foo = bar; Html.ExecuteTemplate(foo," + TestTemplateCode + "); }",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code(" var foo = bar; Html.ExecuteTemplate(foo, ").AsStatement(), Factory.Code(" var foo = bar; Html.ExecuteTemplate(foo, ")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
TestTemplate(), TestTemplate(),
Factory.Code("); ").AsStatement(), Factory.Code("); ").AsStatement(),
Factory.MetaCode("}").Accepts(AcceptedCharacters.None) Factory.MetaCode("}").Accepts(AcceptedCharacters.None)
@ -238,7 +245,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
ParseBlockTest("{ var foo = bar; Html.ExecuteTemplate(foo," + TestTemplateCode + "," + TestTemplateCode + "); }", ParseBlockTest("{ var foo = bar; Html.ExecuteTemplate(foo," + TestTemplateCode + "," + TestTemplateCode + "); }",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code(" var foo = bar; Html.ExecuteTemplate(foo, ").AsStatement(), Factory.Code(" var foo = bar; Html.ExecuteTemplate(foo, ")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
TestTemplate(), TestTemplate(),
Factory.Code(", ").AsStatement(), Factory.Code(", ").AsStatement(),
TestTemplate(), TestTemplate(),
@ -253,7 +262,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
ParseBlockTest("{ var foo = bar; Html.ExecuteTemplate(foo," + TestNestedTemplateCode + "); }", ParseBlockTest("{ var foo = bar; Html.ExecuteTemplate(foo," + TestNestedTemplateCode + "); }",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code(" var foo = bar; Html.ExecuteTemplate(foo, ").AsStatement(), Factory.Code(" var foo = bar; Html.ExecuteTemplate(foo, ")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
TestNestedTemplate(), TestNestedTemplate(),
Factory.Code("); ").AsStatement(), Factory.Code("); ").AsStatement(),
Factory.MetaCode("}").Accepts(AcceptedCharacters.None) Factory.MetaCode("}").Accepts(AcceptedCharacters.None)

View File

@ -19,7 +19,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
ParseBlockTest("{ List< }", ParseBlockTest("{ List< }",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code(" List< ").AsStatement(), Factory.Code(" List< ")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
Factory.MetaCode("}").Accepts(AcceptedCharacters.None))); Factory.MetaCode("}").Accepts(AcceptedCharacters.None)));
} }
@ -77,7 +79,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
+ "}", + "}",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code(Environment.NewLine + " ").AsStatement(), Factory.Code(Environment.NewLine + " ")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
new MarkupBlock( new MarkupBlock(
new MarkupTagBlock( new MarkupTagBlock(
Factory.Markup("<p>").Accepts(AcceptedCharacters.None)), Factory.Markup("<p>").Accepts(AcceptedCharacters.None)),
@ -98,7 +102,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
+ "}", + "}",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code(Environment.NewLine + " ").AsStatement(), Factory.Code(Environment.NewLine + " ")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
new MarkupBlock( new MarkupBlock(
Factory.MarkupTransition(), Factory.MarkupTransition(),
new MarkupTagBlock( new MarkupTagBlock(
@ -122,7 +128,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
+ "}", + "}",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code(Environment.NewLine + " ").AsStatement(), Factory.Code(Environment.NewLine + " ")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
new MarkupBlock( new MarkupBlock(
Factory.MarkupTransition(), Factory.MarkupTransition(),
Factory.MetaMarkup(":", HtmlSymbolType.Colon), Factory.MetaMarkup(":", HtmlSymbolType.Colon),
@ -152,13 +160,15 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
new StatementBlock( new StatementBlock(
Factory.CodeTransition(), Factory.CodeTransition(),
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.EmptyCSharp().AsStatement(), Factory.EmptyCSharp()
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
Factory.MetaCode("}").Accepts(AcceptedCharacters.None) Factory.MetaCode("}").Accepts(AcceptedCharacters.None)
), ),
Factory.Markup(Environment.NewLine) Factory.Markup(Environment.NewLine)
.With(new SingleLineMarkupEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString, AcceptedCharacters.None)) .Accepts(AcceptedCharacters.None)
) )
), ),
Factory.Code(")") Factory.Code(")")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords) .AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharacters.NonWhiteSpace) .Accepts(AcceptedCharacters.NonWhiteSpace)
@ -291,7 +301,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
ParseBlockTest("{ if(foo) { <p>Bar</p> } else if(bar) { <p>Baz</p> } else { <p>Boz</p> } }", ParseBlockTest("{ if(foo) { <p>Bar</p> } else if(bar) { <p>Baz</p> } else { <p>Boz</p> } }",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code(" if(foo) {").AsStatement(), Factory.Code(" if(foo) {")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
new MarkupBlock( new MarkupBlock(
Factory.Markup(" "), Factory.Markup(" "),
new MarkupTagBlock( new MarkupTagBlock(
@ -421,7 +433,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
+ "} }", + "} }",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code($" switch(foo) {{{Environment.NewLine} case 0:{Environment.NewLine}").AsStatement(), Factory.Code($" switch(foo) {{{Environment.NewLine} case 0:{Environment.NewLine}")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
new MarkupBlock( new MarkupBlock(
Factory.Markup(" "), Factory.Markup(" "),
new MarkupTagBlock( new MarkupTagBlock(
@ -501,7 +515,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
ParseBlockTest("{ for(int i = 0; i < 10; i++) { <p>Foo</p> } }", ParseBlockTest("{ for(int i = 0; i < 10; i++) { <p>Foo</p> } }",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code(" for(int i = 0; i < 10; i++) {").AsStatement(), Factory.Code(" for(int i = 0; i < 10; i++) {")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
new MarkupBlock( new MarkupBlock(
Factory.Markup(" "), Factory.Markup(" "),
new MarkupTagBlock( new MarkupTagBlock(
@ -541,12 +557,15 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
+ "} } zoop", + "} } zoop",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code(" if(foo) {").AsStatement(), Factory.Code(" if(foo) {")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
new MarkupBlock( new MarkupBlock(
Factory.Markup(" "), Factory.Markup(" "),
Factory.MarkupTransition(), Factory.MarkupTransition(),
Factory.MetaMarkup(":", HtmlSymbolType.Colon), Factory.MetaMarkup(":", HtmlSymbolType.Colon),
Factory.Markup("Bar" + Environment.NewLine).Accepts(AcceptedCharacters.None) Factory.Markup("Bar" + Environment.NewLine).Accepts(AcceptedCharacters.None)
.With(new SingleLineMarkupEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString, AcceptedCharacters.None))
), ),
Factory.Code("} ").AsStatement(), Factory.Code("} ").AsStatement(),
Factory.MetaCode("}").Accepts(AcceptedCharacters.None))); Factory.MetaCode("}").Accepts(AcceptedCharacters.None)));
@ -576,7 +595,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
ParseBlockTest("{ if (i > 0) { <text>;</text> } }", ParseBlockTest("{ if (i > 0) { <text>;</text> } }",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code(" if (i > 0) {").AsStatement(), Factory.Code(" if (i > 0) {")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
new MarkupBlock( new MarkupBlock(
Factory.Markup(" "), Factory.Markup(" "),
new MarkupTagBlock( new MarkupTagBlock(
@ -606,7 +627,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
+ "}", + "}",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code($"{Environment.NewLine} if(true) {{{Environment.NewLine}").AsStatement(), Factory.Code($"{Environment.NewLine} if(true) {{{Environment.NewLine}")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
new MarkupBlock( new MarkupBlock(
Factory.Markup(" "), Factory.Markup(" "),
Factory.MarkupTransition(), Factory.MarkupTransition(),

View File

@ -21,7 +21,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
Factory.MetaCode("{") Factory.MetaCode("{")
.Accepts(AcceptedCharacters.None), .Accepts(AcceptedCharacters.None),
Factory.Code(" foo(); ") Factory.Code(" foo(); ")
.AsStatement(), .AsStatement()
.AutoCompleteWith(autoCompleteString: null),
Factory.MetaCode("}") Factory.MetaCode("}")
.Accepts(AcceptedCharacters.None) .Accepts(AcceptedCharacters.None)
)); ));
@ -33,7 +34,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
ParseBlockTest("{@}", ParseBlockTest("{@}",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.EmptyCSharp().AsStatement(), Factory.EmptyCSharp()
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
new ExpressionBlock( new ExpressionBlock(
Factory.CodeTransition(), Factory.CodeTransition(),
Factory.EmptyCSharp().AsImplicitExpression(KeywordSet, acceptTrailingDot: true).Accepts(AcceptedCharacters.NonWhiteSpace) Factory.EmptyCSharp().AsImplicitExpression(KeywordSet, acceptTrailingDot: true).Accepts(AcceptedCharacters.NonWhiteSpace)
@ -53,7 +56,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
ParseBlockTest("{@.}", ParseBlockTest("{@.}",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.EmptyCSharp().AsStatement(), Factory.EmptyCSharp()
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
new ExpressionBlock( new ExpressionBlock(
Factory.CodeTransition(), Factory.CodeTransition(),
Factory.EmptyCSharp().AsImplicitExpression(KeywordSet, acceptTrailingDot: true).Accepts(AcceptedCharacters.NonWhiteSpace) Factory.EmptyCSharp().AsImplicitExpression(KeywordSet, acceptTrailingDot: true).Accepts(AcceptedCharacters.NonWhiteSpace)
@ -75,7 +80,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
+ "}", + "}",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code(Environment.NewLine + " ").AsStatement(), Factory.Code(Environment.NewLine + " ")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
new ExpressionBlock( new ExpressionBlock(
Factory.CodeTransition(), Factory.CodeTransition(),
Factory.EmptyCSharp().AsImplicitExpression(KeywordSet, acceptTrailingDot: true).Accepts(AcceptedCharacters.NonWhiteSpace) Factory.EmptyCSharp().AsImplicitExpression(KeywordSet, acceptTrailingDot: true).Accepts(AcceptedCharacters.NonWhiteSpace)
@ -94,7 +101,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
+ "}", + "}",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.EmptyCSharp().AsStatement(), Factory.EmptyCSharp()
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
new ExpressionBlock( new ExpressionBlock(
Factory.CodeTransition(), Factory.CodeTransition(),
Factory.Code("foo.").AsImplicitExpression(KeywordSet, acceptTrailingDot: true).Accepts(AcceptedCharacters.NonWhiteSpace)), Factory.Code("foo.").AsImplicitExpression(KeywordSet, acceptTrailingDot: true).Accepts(AcceptedCharacters.NonWhiteSpace)),
@ -109,7 +118,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
+ "}", + "}",
new StatementBlock( new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.EmptyCSharp().AsStatement(), Factory.EmptyCSharp()
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
new ExpressionBlock( new ExpressionBlock(
Factory.CodeTransition(), Factory.CodeTransition(),
Factory.Code("foo.").AsImplicitExpression(KeywordSet, acceptTrailingDot: true).Accepts(AcceptedCharacters.NonWhiteSpace)), Factory.Code("foo.").AsImplicitExpression(KeywordSet, acceptTrailingDot: true).Accepts(AcceptedCharacters.NonWhiteSpace)),

View File

@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using Microsoft.AspNet.Razor.Editor;
using Microsoft.AspNet.Razor.Parser;
using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.Parser.SyntaxTree;
using Microsoft.AspNet.Razor.Test.Framework; using Microsoft.AspNet.Razor.Test.Framework;
using Microsoft.AspNet.Razor.Tokenizer.Symbols; using Microsoft.AspNet.Razor.Tokenizer.Symbols;
@ -20,7 +22,10 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
Factory.MarkupTransition() Factory.MarkupTransition()
.Accepts(AcceptedCharacters.None), .Accepts(AcceptedCharacters.None),
Factory.MetaMarkup(":", HtmlSymbolType.Colon), Factory.MetaMarkup(":", HtmlSymbolType.Colon),
Factory.Markup(" "), Factory.Markup(" ")
.With(new SingleLineMarkupEditHandler(
CSharpLanguageCharacteristics.Instance.TokenizeString,
AcceptedCharacters.Any)),
new StatementBlock( new StatementBlock(
Factory.CodeTransition() Factory.CodeTransition()
.Accepts(AcceptedCharacters.None), .Accepts(AcceptedCharacters.None),

View File

@ -36,7 +36,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.Html
new StatementBlock( new StatementBlock(
Factory.CodeTransition(), Factory.CodeTransition(),
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code(Environment.NewLine).AsStatement(), Factory.Code(Environment.NewLine)
.AsStatement()
.AutoCompleteWith("}"),
new MarkupBlock( new MarkupBlock(
new MarkupTagBlock( new MarkupTagBlock(
Factory.Markup("<"))))), Factory.Markup("<"))))),
@ -56,7 +58,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.Html
new StatementBlock( new StatementBlock(
Factory.CodeTransition(), Factory.CodeTransition(),
Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
Factory.Code(Environment.NewLine).AsStatement(), Factory.Code(Environment.NewLine)
.AsStatement()
.AutoCompleteWith("}"),
new MarkupBlock( new MarkupBlock(
new MarkupTagBlock( new MarkupTagBlock(
Factory.Markup("<" + Environment.NewLine)) Factory.Markup("<" + Environment.NewLine))

View File

@ -257,7 +257,8 @@ namespace Microsoft.AspNet.Razor.Test.Parser.Html
Factory.EmptyHtml(), Factory.EmptyHtml(),
new SectionBlock(new SectionCodeGenerator("Foo"), new SectionBlock(new SectionCodeGenerator("Foo"),
Factory.CodeTransition(), Factory.CodeTransition(),
Factory.MetaCode("section Foo {"), Factory.MetaCode("section Foo {")
.AutoCompleteWith(autoCompleteString: null, atEndOfSpan: true),
new MarkupBlock( new MarkupBlock(
Factory.Markup(" "), Factory.Markup(" "),
BlockFactory.MarkupTagBlock("<script>"), BlockFactory.MarkupTagBlock("<script>"),

View File

@ -107,7 +107,7 @@ namespace Microsoft.AspNet.Razor.Test.Parser.Html
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords) .AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharacters.NonWhiteSpace)), .Accepts(AcceptedCharacters.NonWhiteSpace)),
Factory.Markup(" Baz" + Environment.NewLine) Factory.Markup(" Baz" + Environment.NewLine)
.With(new SingleLineMarkupEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString, AcceptedCharacters.None)))); .Accepts(AcceptedCharacters.None)));
} }
[Fact] [Fact]

View File

@ -50,7 +50,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.PartialParsing
new StatementBlock( new StatementBlock(
factory.CodeTransition(), factory.CodeTransition(),
factory.MetaCode("{").Accepts(AcceptedCharacters.None), factory.MetaCode("{").Accepts(AcceptedCharacters.None),
factory.Code(Environment.NewLine + " ").AsStatement(), factory.Code(Environment.NewLine + " ")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
new ExpressionBlock( new ExpressionBlock(
factory.CodeTransition(), factory.CodeTransition(),
factory.Code("DateTime..Now") factory.Code("DateTime..Now")
@ -106,7 +108,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.PartialParsing
new StatementBlock( new StatementBlock(
factory.CodeTransition(), factory.CodeTransition(),
factory.MetaCode("{").Accepts(AcceptedCharacters.None), factory.MetaCode("{").Accepts(AcceptedCharacters.None),
factory.Code(Environment.NewLine + " ").AsStatement(), factory.Code(Environment.NewLine + " ")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
new ExpressionBlock( new ExpressionBlock(
factory.CodeTransition(), factory.CodeTransition(),
factory.Code(expectedCode) factory.Code(expectedCode)
@ -166,7 +170,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.PartialParsing
new StatementBlock( new StatementBlock(
factory.CodeTransition(), factory.CodeTransition(),
factory.MetaCode("{").Accepts(AcceptedCharacters.None), factory.MetaCode("{").Accepts(AcceptedCharacters.None),
factory.Code(Environment.NewLine + " ").AsStatement(), factory.Code(Environment.NewLine + " ")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
new ExpressionBlock( new ExpressionBlock(
factory.CodeTransition(), factory.CodeTransition(),
factory.Code(expectedCode) factory.Code(expectedCode)
@ -352,7 +358,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.PartialParsing
new StatementBlock( new StatementBlock(
factory.CodeTransition(), factory.CodeTransition(),
factory.MetaCode("{").Accepts(AcceptedCharacters.None), factory.MetaCode("{").Accepts(AcceptedCharacters.None),
factory.Code(Environment.NewLine + " ").AsStatement(), factory.Code(Environment.NewLine + " ")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
new ExpressionBlock( new ExpressionBlock(
factory.CodeTransition(), factory.CodeTransition(),
factory.Code("food") factory.Code("food")
@ -379,7 +387,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.PartialParsing
new StatementBlock( new StatementBlock(
factory.CodeTransition(), factory.CodeTransition(),
factory.MetaCode("{").Accepts(AcceptedCharacters.None), factory.MetaCode("{").Accepts(AcceptedCharacters.None),
factory.Code(Environment.NewLine + " ").AsStatement(), factory.Code(Environment.NewLine + " ")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
new ExpressionBlock( new ExpressionBlock(
factory.CodeTransition(), factory.CodeTransition(),
factory.Code("foo.d") factory.Code("foo.d")
@ -406,7 +416,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.PartialParsing
new StatementBlock( new StatementBlock(
factory.CodeTransition(), factory.CodeTransition(),
factory.MetaCode("{").Accepts(AcceptedCharacters.None), factory.MetaCode("{").Accepts(AcceptedCharacters.None),
factory.Code(Environment.NewLine + " ").AsStatement(), factory.Code(Environment.NewLine + " ")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
new ExpressionBlock( new ExpressionBlock(
factory.CodeTransition(), factory.CodeTransition(),
factory.Code(@"foo.") factory.Code(@"foo.")
@ -536,7 +548,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.PartialParsing
new StatementBlock( new StatementBlock(
factory.CodeTransition(), factory.CodeTransition(),
factory.MetaCode("{").Accepts(AcceptedCharacters.None), factory.MetaCode("{").Accepts(AcceptedCharacters.None),
factory.EmptyCSharp().AsStatement(), factory.EmptyCSharp()
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
new ExpressionBlock( new ExpressionBlock(
factory.CodeTransition(), factory.CodeTransition(),
factory.Code("foo.b") factory.Code("foo.b")
@ -559,7 +573,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser.PartialParsing
new StatementBlock( new StatementBlock(
factory.CodeTransition(), factory.CodeTransition(),
factory.MetaCode("{").Accepts(AcceptedCharacters.None), factory.MetaCode("{").Accepts(AcceptedCharacters.None),
factory.EmptyCSharp().AsStatement(), factory.EmptyCSharp()
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
new ExpressionBlock( new ExpressionBlock(
factory.CodeTransition(), factory.CodeTransition(),
factory.Code("foo.") factory.Code("foo.")

View File

@ -41,13 +41,15 @@ namespace Microsoft.AspNet.Razor
Assert.Equal(characterIndex, sourceLocation.CharacterIndex); Assert.Equal(characterIndex, sourceLocation.CharacterIndex);
} }
[Fact] [Theory]
public void GetHashCode_ReturnsHashCode_UsingAbsoluteIndex() [InlineData(null)]
[InlineData("some-file")]
public void GetHashCode_ReturnsSameValue_WhenEqual(string path)
{ {
// Arrange // Arrange
var sourceLocationA = new SourceLocation(10, 3, 4); var sourceLocationA = new SourceLocation(path, 10, 3, 4);
var sourceLocationB = new SourceLocation(10, 45, 8754); var sourceLocationB = new SourceLocation(path, 10, 3, 4);
var sourceLocationC = new SourceLocation(12, 45, 8754); var sourceLocationC = new SourceLocation(path, 10, 45, 8754);
// Act // Act
var hashCodeA = sourceLocationA.GetHashCode(); var hashCodeA = sourceLocationA.GetHashCode();
@ -56,29 +58,11 @@ namespace Microsoft.AspNet.Razor
// Assert // Assert
Assert.Equal(hashCodeA, hashCodeB); Assert.Equal(hashCodeA, hashCodeB);
Assert.NotEqual(hashCodeA, hashCodeC); Assert.Equal(hashCodeA, hashCodeC);
} }
[Fact] [Fact]
public void GetHashCode_ReturnsHashCode_UsingFilePathAndAbsoluteIndex_WhenFilePathIsNonNull() public void Equals_ReturnsTrue_FilePathsNullAndAbsoluteIndicesMatch()
{
// Arrange
var sourceLocationA = new SourceLocation("some-path", 3, 53, 94);
var sourceLocationB = new SourceLocation("some-path", 3, 43, 87);
var sourceLocationC = new SourceLocation(3, 53, 94);
// Act
var hashCodeA = sourceLocationA.GetHashCode();
var hashCodeB = sourceLocationB.GetHashCode();
var hashCodeC = sourceLocationC.GetHashCode();
// Assert
Assert.Equal(hashCodeA, hashCodeB);
Assert.NotEqual(hashCodeA, hashCodeC);
}
[Fact]
public void Equal_ReturnsFalse_IfIndexesDiffer()
{ {
// Arrange // Arrange
var sourceLocationA = new SourceLocation(10, 3, 4); var sourceLocationA = new SourceLocation(10, 3, 4);
@ -88,11 +72,11 @@ namespace Microsoft.AspNet.Razor
var result = sourceLocationA.Equals(sourceLocationB); var result = sourceLocationA.Equals(sourceLocationB);
// Assert // Assert
Assert.False(result); Assert.True(result);
} }
[Fact] [Fact]
public void Equal_ReturnsFalse_IfFilePathIsDifferent() public void Equals_ReturnsFalse_IfFilePathIsDifferent()
{ {
// Arrange // Arrange
var sourceLocationA = new SourceLocation(10, 3, 4); var sourceLocationA = new SourceLocation(10, 3, 4);
@ -108,7 +92,7 @@ namespace Microsoft.AspNet.Razor
[Theory] [Theory]
[InlineData(null)] [InlineData(null)]
[InlineData("some-file")] [InlineData("some-file")]
public void Equal_ReturnsTrue_IfFilePathAndIndexesAreSame(string path) public void Equals_ReturnsTrue_IfFilePathAndIndexesAreSame(string path)
{ {
// Arrange // Arrange
var sourceLocationA = new SourceLocation(path, 10, 3, 4); var sourceLocationA = new SourceLocation(path, 10, 3, 4);